HDU 4455 DP+树状数组

题意

给一串数字,里面有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]);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zhenlingcn/article/details/78172683