首页 > 代码库 > HDU 4455 Substrings --递推+树状数组优化

HDU 4455 Substrings --递推+树状数组优化

题意: 给一串数字,给q个查询,每次查询长度为w的所有子串中不同的数字个数之和为多少。

解法:先预处理出D[i]为: 每个值的左边和它相等的值的位置和它的位置的距离,如果左边没有与他相同的,设为n+8(看做无穷)。

考虑已知w=k的答案,推w = k+1时,这时每个区间都将增加一个数,即后n-k个数会增加到这些区间中,容易知道,如果区间内有该数,那么个数不会加1,,即D[i] > k时,结果++,即查询后n-k个数有多少个D[i] > k 的数即为要加上的数,然后最后面还会损失一个区间,损失的是不能增加一个数的那个区间,随着w的增加,该区间会向左边伸展,所以记录一下该区间有多少个不同的数即可。 求区间内有多少个D[i]>k可以用树状数组先求有多少个D[i]<=k(getsum(k)),然后区间长度减一下即可。

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#define lll __int64using namespace std;#define N 1000107int c[N],n,D[N],Last,vis[N],a[N],pos[N],L[N];lll sum[N];int lowbit(int x) { return x&-x; }void modify(int x,int val){    while(x <= n+10)    {        c[x]+=val;        x += lowbit(x);    }}int getsum(int x){    int res = 0;    while(x > 0)    {        res += c[x];        x -= lowbit(x);    }    return res;}int main(){    int q,i,j,w;    while(scanf("%d",&n)!=EOF && n)    {        for(i=1;i<=n;i++)            scanf("%d",&a[i]);        memset(pos,0,sizeof(pos));        for(i=1;i<=n;i++)        {            int x = a[i];            D[i] = i-pos[x];            if(D[i] == i) D[i] = n+8;            pos[x] = i;        }        memset(c,0,sizeof(c));        memset(vis,0,sizeof(vis));        for(i=2;i<=n;i++)            modify(D[i],1);        sum[1] = n;        Last = 1;        vis[a[n]] = 1;        for(i=2;i<=n;i++)        {            sum[i] = sum[i-1]+(n-i+1-getsum(i-1))-Last;            modify(D[i],-1);            if(!vis[a[n-i+1]])            {                vis[a[n-i+1]] = 1;                Last++;            }        }        scanf("%d",&q);        while(q--)        {            scanf("%d",&w);            printf("%I64d\n",sum[w]);        }    }    return 0;}
View Code

 

HDU 4455 Substrings --递推+树状数组优化