2012杭州赛区(浙江理工大学)C - Substrings

22 篇文章 0 订阅


C - Substrings
Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

XXX has an array of length n. XXX wants to know that, for a given w, what is the sum of the distinct elements’ number in all substrings of length w. For example, the array is { 1 1 2 3 4 4 5 } When w = 3, there are five substrings of length 3. They are (1,1,2),(1,2,3),(2,3,4),(3,4,4),(4,4,5) 
The distinct elements’ number of those five substrings are 2,3,3,2,2. 
So the sum of the distinct elements’ number should be 2+3+3+2+2 = 12
 

Input

There are several test cases. 
Each test case starts with a positive integer n, the array length. The next line consists of n integers a  1,a  2…a  n, representing the elements of the array. 
Then there is a line with an integer Q, the number of queries. At last Q lines follow, each contains one integer w, the substring length of query. The input data ends with n = 0 For all cases, 0<w<=n<=10  6, 0<=Q<=10  4, 0<= a  1,a  2…a  n <=10  6
 

Output

For each test case, your program should output exactly Q lines, the sum of the distinct number in all substrings of length w for each query.
 

Sample Input

      
      
7 1 1 2 3 4 4 5 3 1 2 3 0
 

Sample Output

      
      
7 10 12
题意:题目意思并不难,输入n个数,然后输入q个数,每个数代表集合的元素个数,集合的元素必须是连续的,然后求所有集合不同元素个数的总和。

思路:这道题可以用dp解决,我们可以观察到范围每增加1,集合的个数就会减少1,而且减少的是上一个状态的最后一个集合,我们用一个数组last来储存每个状态最后一个集合不同元素的个数。然后就是增加的那部分,如何知道呢?我们可以求出每个数距离上一次出现时下标差是多少,例如数字4,它第一次出现在下标2,第二次出现在了下标7,距离是5,如此类推。首先,我们用数组sum来统计各个距离一共有多少个数,因为只要距离大于等于该状态的范围我们就可以添加到该状态上。例如,dp[2]表示每两个数为一个集合,所有集合不同元素的个数总和。那么dp[2]=dp[1]-last[1]+(距离大于等于2的数的个数)。所以我们再用一个循环把sum从后累加到头就可以用sum来表示距离大于等于i时,有多少个数。最后用递推式:dp[i]=dp[i-1]-last[i-1]+sum[i];下面给代码。

#include<iostream>
#include<stack>
#include<cstring>
#include<map>
#include<string>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<utility>
using namespace std;
#define maxn 1000005
typedef long long LL;
int a[maxn], sum[maxn], last[maxn],vis[maxn];//sum表示距离大于等于i的元素有多少个,last表示q=i时,最后一个集合不同元素的个数。
LL dp[maxn];
int main() {
	int n;
	a[0] = -1;
	while (scanf("%d", &n) != EOF) {
		if (!n)
			break;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
		}	
		for (int i = 1; i <= n; i++) {
			sum[i - vis[a[i]]]++;
			vis[a[i]] = i;
		}
		memset(vis, 0, sizeof(vis));
		last[1] = 1;
		vis[a[n]] = 1;
		for (int i = n - 1,j=2; i >= 1; i--,j++) {
			last[j] = last[j - 1];
			if (vis[a[i]]) 
				continue;
			last[j]++;
			vis[a[i]] = 1;
		}
		for (int i = n - 1; i >= 1; i--) {
			sum[i] += sum[i + 1];
		}
		dp[1] = n;
		for (int i = 2; i <= n; i++) {
			dp[i] = dp[i - 1] - last[i - 1] + sum[i];
		}
		int q;
		scanf("%d", &q);
		while (q--) {
			int re;
			scanf("%d", &re);
			printf("%lld\n", dp[re]);
		}
		memset(vis, 0, sizeof(vis));
		memset(sum, 0, sizeof(sum));
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值