题意:给定一个序列ai,个数为n。再给出一系列w;对于每个w,求序列中,所有长度为w的连续子串中的权值和,子串权值为子串中不同数的个数
去了几个头文件,然后终于A了
以样例为例说明:
1 1 2 3 4 4 5;
明显dp[1]=n=7;
长度为1的时候有7个区间。从长度为1到长度为2,就是把前6个区间往后增加一个数,把最后一个区间去掉。
增加的6个数要看在该区间是否出现过,只要看它上一个相等的元素距离是否大于2
所以dp[2]=dp[1]-1+4;
以此类推就可以得出所以的dp值了。
dp[i]=dp[i-1]-A+B;
减的A是最后一个长度为i-1的区间的不同数的个数,这个很容易预处理得出来。
加的B是第t个数到它上一个数的距离大于i-1的个数.
这个B值也容易得出。
用s[i]表示离上一个数的距离为i的个数,不断减掉就得到B了。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 #define ts printf("***** "); 5 #define cl(a) memset(a,0,sizeof(a)) 6 const int maxn=1000007; 7 long long dp[maxn]; 8 int dis[maxn]; //第i个数和前面最近相同的数的距离 例如1,2,1 d[3]=2 9 int r[maxn]; //和前面最近相同的数的距离为i的个数 10 int vis[maxn]; 11 int lastp[maxn]; //数i的最近上一个位置 12 int a[maxn]; 13 int dif[maxn]; //从n-i+1到n不同的数的个数 14 15 int main() 16 { 17 int i,j,k; 18 #ifndef ONLINE_JUDGE 19 freopen("1.in","r",stdin); 20 #endif 21 int n; 22 while(scanf("%d",&n)!=EOF) 23 { 24 if(n==0) break; 25 cl(lastp); 26 for(i=1;i<=n;i++) 27 { 28 scanf("%d",a+i); 29 dis[i]=i-lastp[a[i]]; 30 lastp[a[i]]=i; 31 } 32 cl(r); 33 for(i=1;i<=n;i++) 34 { 35 r[dis[i]]++; 36 } 37 cl(dif); 38 cl(vis); 39 int t=0; 40 for(i=n;i>0;i--) 41 { 42 if(!vis[a[i]]) 43 { 44 vis[a[i]]=1; 45 t++; 46 } 47 dif[n-i+1]=t; 48 } 49 int sum=n; 50 cl(dp); 51 dp[1]=n; 52 for(i=2;i<=n;i++) 53 { 54 dp[i]+=dp[i-1]-dif[i-1];//减掉最后一个区间的种类数 55 sum-=r[i-1]; 56 dp[i]+=sum; 57 } 58 int q,tt; 59 scanf("%d",&q); 60 while(q--) 61 { 62 scanf("%d",&tt); 63 printf("%I64d ",dp[tt]); 64 } 65 } 66 return 0; 67 }