spoj D-query 区间不同数个数 主席树||离线+树状数组

来源:互联网 发布:手机玩lol软件 编辑:程序博客网 时间:2024/04/28 13:08

把区间统计转化为前缀和,这里的前缀和不是普通的前缀和,对相同的ai,只有最右边那个才是有效的。

举个栗子:1 2 2 1 3 这样一个序列有效是这个样子 * * 2 1 3 ,因为1在后面出现过所以前一个无效,同理 2。前缀和则是0 0 1 2 3 ,那么对区间[1,5],[2,5],[3,5],[4,5],..[x,5] 我们都可以用sum[5]-sum[x-1]来求。

这里的一个问题是前缀和是对右端点确定的情况,所以只用一棵线段树或树状数组来维护的话就要离线,对查询的右端点进行排序。

如果强制在线怎么做呢,那就是主席树,简单理解为对每一个前缀建一棵线段树(这就是主席树做的事情),然后查询就是到右端点对应那棵树里去查询就ok了。



离线+树状数组

#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <algorithm>#include <map>using namespace std;const int maxn=30000+5;map<int,int> mp;int data[maxn];int a[maxn];int ans[200000+5];struct node{    int l,r,id;    bool operator<(node t)const{        return r<t.r;    }}q[200000+5];int sum(int i){    int ans=0;    while(i>0){        ans+=data[i];        i-=i&-i;    }    return ans;}void add(int i,int x){    while(i<maxn){        data[i]+=x;        i+=i&-i;    }}int main(){    int n;    while(~scanf("%d",&n)){        fill(data,data+n+2,0);        mp.clear();        for(int i=1;i<=n;i++){            scanf("%d",&a[i]);        }        int m;        scanf("%d",&m);        for(int i=0;i<m;i++){            scanf("%d%d",&q[i].l,&q[i].r);            q[i].id=i;        }        sort(q,q+m);        int pre=1;        for(int i=0;i<m;i++){            for(int j=pre;j<=q[i].r;j++){                if(mp[a[j]]!=0){                    add(mp[a[j]],-1);                }                add(j,1);                mp[a[j]]=j;            }            pre=q[i].r+1;            ans[q[i].id]=sum(q[i].r)-sum(q[i].l-1);        }        for(int i=0;i<m;i++){            printf("%d\n",ans[i]);        }    }    return 0;}

主席树

#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <algorithm>#include <map>#define mid (l+r>>1)using namespace std;const int maxn=30000+5;int a[maxn],root[maxn];int data[maxn*20],ls[maxn*20],rs[maxn*20];int cnt;map<int,int> mp;int build(int l,int r){    int t=cnt++;    data[t]=0;    if(l==r)return t;    ls[t]=build(l,mid);    rs[t]=build(mid+1,r);    return t;}int update(int p,int l,int r,int v,int w){    int t=cnt++;    data[t]=data[p]+w;    if(l==r) return t;    if(v<=mid){        rs[t]=rs[p];        ls[t]=update(ls[p],l,mid,v,w);    }    else{        ls[t]=ls[p];        rs[t]=update(rs[p],mid+1,r,v,w);    }    return t;}int query(int t,int l,int r,int v){    if(l==r)return data[t];    if(v<=mid)return query(ls[t],l,mid,v)+data[rs[t]];    else return query(rs[t],mid+1,r,v);}int main(){    int n;    while(~scanf("%d",&n)){        cnt=1;mp.clear();        for(int i=1;i<=n;i++){            scanf("%d",&a[i]);        }        root[0]=build(1,n);        for(int i=1;i<=n;i++){            if(mp[a[i]]==0)                root[i]=update(root[i-1],1,n,i,1);            else {                int tmp=update(root[i-1],1,n,mp[a[i]],-1);                root[i]=update(tmp,1,n,i,1);            }            mp[a[i]]=i;        }        int m;        scanf("%d",&m);        for(int i=0;i<m;i++){            int l,r;            scanf("%d%d",&l,&r);            printf("%d\n",query(root[r],1,n,l));        }    }    return 0;}


0 0
原创粉丝点击