hdu4455之树状数组+DP

Substrings

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1571    Accepted Submission(s): 459


Problem 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
/*分析:
dp[i]表示区间长度为i时的个数
则从区间长度i-1到i减少了最后一个i-1长度的区间
记last[i]表示最后一个区间长度为i的不同数的个数
所以dp[i]=dp[i-1]-last[i-1]
同时从区间i-1长度到i增加了n-i+1个数
增加的数为a[i],a[i+1],a[i+2]...a[n]
则增加的这些数表示区间长度为i且以a[i...n]结尾
有多少是不能增加的呢?
比如a[1]~a[i-1]存在a[i],以a[i]结尾的区间长度为i的区间就不用增加a[i]这个数 
在这里预处理时以a[i]结尾的区间长度为l~r不能增加
则l=i-pos[a[i]]+1;//pos[a[i]]表示i位置前出现的a[i]
r=i-1+1+1
用树状数组能快速的累加
所以最终增加的是n-i+1-Query(i) 
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 99999999
typedef __int64 LL;
using namespace std;

const int MAX=1000000+10;
int n,m;
int pos[MAX],a[MAX];
LL dp[MAX],last[MAX],c[MAX];

int lowbit(int x){
	return x&(-x);
}

void Update(int x,int d){
	while(x<=n){
		c[x]+=d;
		x+=lowbit(x);
	}
}

LL Query(int x){
	LL sum=0;
	while(x>0){
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}

int main(){
	while(~scanf("%d",&n),n){
		memset(pos,-1,sizeof pos);
		memset(c,0,sizeof c);
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
			if(pos[a[i]] != -1){
				Update(i-pos[a[i]]+1,1);
				Update(i-1+2,-1);
			}
			pos[a[i]]=i;
		}
		memset(pos,-1,sizeof pos);
		memset(last,0,sizeof last);//last记录最后面的长度为i的不同数的个数
		for(int i=n;i>=1;--i){
			last[n-i+1]=last[n-i];
			if(pos[a[i]] == -1)++last[n-i+1];
			pos[a[i]]=i;
		} 
		dp[1]=n;
		for(int i=2;i<=n;++i){
			dp[i]=dp[i-1]+(n-i+1-Query(i))-last[i-1];
		}
		scanf("%d",&m);
		for(int i=0;i<m;++i){
			scanf("%d",&n);
			printf("%I64d\n",dp[n]);
		}
	}
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值