题意
给一串数字,里面有N个数字。有Q次查询,每次查询长度为X的串,不相同的数字个数之和。
题解
很难看出来是DP,不过考虑到查询问题,并且无法用普通的数据结构进行解决,同时长度之间存在利用关系,因此可以用DP解决。
首先对于长度为1的串,很明显可以得到dp[1]=n。对于长度为2的串,我们首先要删除末尾的一个长度为1的串,然后对于每个串再加上一个值,如果新增了一个数字,那么这个值就是1,否则就是0。然后对于长度为X的串,可以以此类推。
首先对于末尾长度为X的字符串的影响,我们可以预处理一下。对于每次加上的一个值,我们可以考虑一下什么情况下不会加。如果对于一个数字,可以在前D位置之内找到一个同样的数字,那么这个数字对于长度D是没有任何意义的,因此我们就可以看一下这个数字与上一个数字的距离,这里用树状数组维护一下,对于1到距离的值,都选择+1。这样就代表这个数字会对长度小于距离的DP产生影响(肯定存在一个区间补上这个数字,并且这个数字以前不在区间内)。这样的话,就可以预处理出来0-10^6所有情况的值,查询的时候直接输出就可以了。
代码
#include<bits/stdc++.h>
#define LL long long
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(t) while(t)
#define MEM(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MAXN 1000010
using namespace std;
int a[MAXN];
LL dp[MAXN];
int num[MAXN],last[MAXN],sum[MAXN];
bool vis[MAXN];
int n;
int lowbit(int x){return x&(-x);}
void add(int x,int v){
for(;x<=n;x+=lowbit(x)){
num[x]+=v;
}
}
void update(int l,int r){
if(r<l) return ;
add(l,1);
add(r+1,-1);
}
int query(int x){
int sum=0;
for(;x>0;x-=lowbit(x)){
sum+=num[x];
}
return sum;
}
int main() {
W(~scanf("%d",&n)){
if(n==0) break;
MEM(vis,false);
MEM(dp,0);
MEM(last,-1);
MEM(num,0);
MEM(sum,0);
UP(i,0,n) scanf("%d",&a[i]);
DOWN(i,n,0){
if(!vis[a[i]]){
sum[n-i]=sum[n-i-1]+1;
vis[a[i]]=true;
}else sum[n-i]=sum[n-i-1];
}
UP(i,0,n){
update(1,i-last[a[i]]);
last[a[i]]=i;
}
dp[1]=n;
UP(i,2,n+1){
dp[i]=dp[i-1]-sum[i-1]+query(i);
// cout<<i<<" "<<sum[i]<<endl;
}
int q;
scanf("%d",&q);
W(q--){
int x;
scanf("%d",&x);
printf("%I64d\n",dp[x]);
}
}
}