Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1161 Accepted Submission(s): 351
题意:
给你一个数组{a1,a2,a3........an}。然后定义了一个询问。给你一个w。要你求出。所有a[i+1],a[i+2]......a[i+w]。中不同元素个数的和。i+w<=n。
思路:
dp[i]表示w为i时的答案。我们考虑dp[i+1]即长度增加一个后的情况。
对于n=3时。dp[3]已知
[1 1 2 ]3 4 4 5
对于n=4时。
[1 1 2 3] 4 4 5
相当于在以前三元集中加入一个数。所以如果该元素出现过dp[4]的值和dp[3]值一样。
如果不一样就要在dp[3]的基础上减一个。但是对于数组的最后三个数已经不能算作dp[4]的值。但dp[3]把它们
算作在内。所以要把他们减出来。我们对于数组中的每一个数。维护一个数组pre[v]表示数值v上次出现的位置。那么i-pre[v]即两数的不重复区间长度。
哎。。。比赛时居然为一个__int64卡了半天。还是不专业呀。都没有分析数据范围。。。还是最后十分钟做出来的。
不然应该有机会做做其它题目的。
详细见代码:
#include <iostream> #include<string.h> #include<stdio.h> using namespace std; const int maxn=1000010; int a[maxn],pre[maxn],len[maxn]; __int64 dp[maxn],rest;//注意数据范围呀!! bool vis[maxn]; int main() { int n,q,w,i; while(scanf("%d",&n),n) { memset(pre,-1,sizeof pre); memset(len,0,sizeof len); memset(vis,0,sizeof vis); for(i=0;i<n;i++) { scanf("%d",a+i); len[i-pre[a[i]]]++;//统计各长度的数目 pre[a[i]]=i; } for(i=n-1;i>=0;i--) len[i]+=len[i+1];//len[i]代表长度大于等于i的个数 dp[0]=0; dp[1]=n; rest=1; vis[a[n-1]]=true; for(i=2;i<=n;i++) { dp[i]=dp[i-1]+len[i]-rest;//rest为长度不足i的部分 if(!vis[a[n-i]]) { rest++; vis[a[n-i]]=true; } } scanf("%d",&q); while(q--) { scanf("%d",&w); printf("%I64d\n",dp[w]); } } return 0; }