测试地址:D-query
题目大意:给一个长为N的数列,Q次询问,每次询问一个区间[i,j]内有多少个不同的数。
做法:用主席树解决问题,将数列上的数字逐个插入,对于第i次操作,在第i个位置+1并生成一个新的版本,表示在第i个位置新出现一个数,然而要排除重复数字的干扰,我们就把下一个出现这个数的位置称为next[i],在第i个版本中的第next[i]个位置-1,然后对于每一个询问[i,j],答案就是第j个版本与第i-1个版本相减后区间[1,j]的和。
Update:这题后来我用莫队算法也做了,题解请看这里。
以下是本人代码:
#include
#include
#include
#include
#include
using namespace std;
int n,q,tot=0,last[1000010]={0},nxt[100010]={0},rt[100010]={0};
struct segnode
{
int lc,rc,sum;
}seg[3000010];
void buildtree(int &no,int l,int r)
{
no=++tot;
seg[no].lc=seg[no].rc=seg[no].sum=0;
if (l==r) return;
int mid=(l+r)>>1;
buildtree(seg[no].lc,l,mid);
buildtree(seg[no].rc,mid+1,r);
}
void modify(int last,int &no,int l,int r,int pos,int val)
{
no=++tot;
seg[no]=seg[last];
if (l==r)
{
seg[no].sum+=val;
return;
}
int mid=(l+r)>>1;
if (pos<=mid) modify(seg[last].lc,seg[no].lc,l,mid,pos,val);
else modify(seg[last].rc,seg[no].rc,mid+1,r,pos,val);
seg[no].sum=seg[seg[no].lc].sum+seg[seg[no].rc].sum;
}
int query(int fnt,int lst,int l,int r,int t)
{
if (r<=t) return seg[lst].sum-seg[fnt].sum;
int tot=0,mid=(l+r)>>1;
tot+=query(seg[fnt].lc,seg[lst].lc,l,mid,t);
if (t>mid) tot+=query(seg[fnt].rc,seg[lst].rc,mid+1,r,t);
return tot;
}
int main()
{
scanf("%d",&n);
buildtree(rt[0],1,n);
for(int i=1,a;i<=n;i++)
{
scanf("%d",&a);
last[a]=nxt[last[a]]=i;
}
for(int i=1;i<=n;i++)
{
modify(rt[i-1],rt[i],1,n,i,1);
if (nxt[i]) modify(rt[i],rt[i],1,n,nxt[i],-1);
}
scanf("%d",&q);
for(int i=1,a,b;i<=q;i++)
{
scanf("%d%d",&a,&b);
printf("%d\n",query(rt[a-1],rt[b],1,n,b));
}
return 0;
}