ZOJ 3872 浙江2015年省赛试题

ZOJ Problem Set – 3872
Beauty of Array
Time Limit: 2 Seconds     
Memory Limit: 65536 KB

Edward has an array A with N integers. He defines the beauty of an array as the summation of all distinct integers in the array. Now Edward wants to know the summation of the beauty of all contiguous subarray of the array A.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains an integer N (1 <= N <= 100000), which indicates the size of the array. The next line contains N positive integers separated by spaces. Every integer is no larger than 1000000.

Output

For each case, print the answer in one line.

Sample Input

3
5
1 2 3 4 5
3
2 3 3
4
2 3 3 2

Sample Output

105
21
38
 

Author:
LIN, Xi

Source: The 12th Zhejiang Provincial Collegiate Programming Contest

        看到问类似于这样求方案数目、出现次数的题目,马上想到的是搜索、贪心、无算法、或动态规划。一看数据十万,那就只能是贪心、无算法或者是动态规划。这道题也不是求最优的,因此咱们考虑无算法或者是动态规划就行了。

        由于我这个人比较懒,所以先考虑不用算法能不能解决问题。如果我们把连续的相同的数字浓缩成一个带权的数字,然后看这个点被覆蓋多少次,以及这个点内部可以有多少条线段,一求和不久完了吗?

        但是这样我们遭遇了一个问题:容易重复计算。也就是说一条覆蓋当前区域的线段,很有可能同时覆蓋了下一个没有考虑过的区域。然后在考虑那个区域的时候我们自然而然又计算了一次。

        一下是“无算法”的错误代码。可以说这个错误还是挺不好找的,如果真的陷入这里了恐怕非得WA一发不可。

       

#include<iostream>
#include<string.h>
using namespace std;

long long int a[1000005];
long long int b[1000005];
long long int pre[1000005];
long long int sum[1000005];

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		memset(b,0,sizeof(b));
		memset(pre,0,sizeof(pre));
		int n;
		cin>>n;
		int cur = 0,p = -1;
		int t;
		for(int i = 1;i<=n;i++)
		{
			cin>>t;
			if(t != p)
			{
				cur ++;
				a[cur] = t;
				b[cur] ++;
				p = t;
			}
			else
			{
				b[cur]++;
			}
		}
		for(int i = 1;i<=cur;i++)
		sum[i] = sum[i-1] + b[i];
		long long int ans = 0;
		for(int i = 1;i<=cur;i++)
		{
			ans += a[i] * ((sum[i-1] - pre[a[i]]) * (sum[cur] - sum[i-1]) + b[i] * (sum[cur] - sum[i]) + b[i] * (b[i] + 1) / 2);
			pre[a[i]] += b[i];
		}
		cout<<ans<<endl;
	}
	return 0;
}

        看来只能上动态规划了。偷懒失败。因为数据量的问题我们恐怕也只能定义一种状态了:以第i个数为结尾如何如何。稍微想想就能知道应该定义成以第i个数结尾的所有子序列的和是多少。那么这样我们就有了一个很简单的转移方法:

        dp[i] = dp[i-1] + x[i] * (i – last[x[i]]);

        last表示的是x[i]这个数字上一次出现在哪个位置,这样可以确保不会重复计算。由于dp[i]只和dp[i-1]有关系,所以我们也可以压缩一下,把一个数组压缩成一个变量进行转移就得了。每次转移之后更新一下总的和就ok。上简短小代码:

       

#include<iostream>
#include<cstring>
#include<string>
#define MAXN 1000005
using namespace std;
int last[MAXN];

int main()
{
    int T, n;
    cin>>T;
    while (T--)
    {
        memset (a, 0, sizeof (a));
        cin>>n;
        int t;
		long long dp = 0, sum = 0;
        for (int i=1; i<=n; ++i)
        {
            scanf ("%d", &t);
            dp = (i - last[t]) * t + dp;
            sum += dp;
            last[t] = i;
        }

        cout<<sum<<endl;
    }
    return 0;
}

        这次教训告诉我,以后就别偷懒了。把所有的思路都想完看看哪个更靠谱再敲吧。这么个水题居然浪费了那么长时间简直是丧心病狂。

点赞