SPOJ DQUERY - D-query
传送门
题意:给定区间内有多少个不同的数。
思路:离线树状数组||主席树
离线树状数组
先将询问按照r排序
然后依次将数放进数组
如果这个数出现过 将之前位置清零,当前位置+1
如果这个是没有出现过
就在当前位置+1
算法的正确性:因为是按照从左到右依次加入的,所以查询r区间里的l一定是正确的
所以只需要将r排序即可
代码如下
#include<cstdio>
#include<algorithm>
const int Maxn=30005;
const int Maxm=200005;
struct node{int l,r,id;}T[Maxm];
int c[Maxn],ans[Maxm],a[Maxn],w,n,m,mp[1000005];
bool cmp(node a,node b){return a.r<b.r;}
inline void update(int x,int v){while(x<=Maxn)c[x]+=v,x+=(x&-x);}
inline int sum(int x,int y)
{
int s=0;for(;x!=0||y!=0;x-=(x&-x),y-=(y&-y))s+=c[x],s-=c[y];return s;
}
inline int read()
{
int res=0,ch,flag=0;
if((ch=getchar())=='-')flag=1;
else if(ch>='0'&&ch<='9')res=ch-'0';
while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-'0';
return flag?-res:res;
}
inline void Out(int a)
{
if(a>9)Out(a/10);
putchar(a%10+'0');
}
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
m=read();
for(int i=0;i<m;i++)T[i].l=read(),T[i].r=read(),T[i].id=i;
std::sort(T,T+m,cmp);
w=1;
for(int i=0;i<m;i++)
{
for(int j=w;j<=T[i].r;j++)
{
if(!mp[a[j]])update(j,1);
else update(mp[a[j]],-1),update(j,1);
mp[a[j]]=j;
}
ans[T[i].id]=sum(T[i].r,T[i].l-1);
w=T[i].r+1;
}
for(int i=0;i<m;i++)Out(ans[i]),puts("");
return 0;
}
主席树
第i颗线段树维护的是位置1-i中有多少个不同的数
询问时只需要在第r颗线段树中询问[l,r]里有多少数就行了
更新线段树的方法 如果这个数i出现过
先定义一个临时的树
然后将这棵树的信息更改为第i-1颗树的信息并且将之前这个数i'从树中删除
然后用这颗临时的树去更新第i个数
如果这个数没有出现过 就直接用第i-1颗树去更新第i颗树
算法的正确性:因为每棵树维护的是[1,i]中不同数的个数,所以在树中询问[l,r]得到的一定是正确的。
代码如下
#include<cstdio>
const int Maxn=30005;
struct node{int l,r,tot;}T[Maxn*20];
int a[Maxn],n,m,l,r,mp[1000006],cnt,rt[Maxn],ans,tmp;
void update(int l,int r,int &x,int y,int k,int t)
{
T[++cnt]=T[y];
x=cnt;
if(l==r){T[x].tot=T[y].tot+t;return;}
int mid=(l+r)>>1;
if(k<=mid)update(l,mid,T[x].l,T[y].l,k,t);
else update(mid+1,r,T[x].r,T[y].r,k,t);
T[x].tot=T[T[x].l].tot+T[T[x].r].tot;
}
void query(int l,int r,int ll,int rr,int x)
{
if(ll<=l&&r<=rr){ans+=T[x].tot;return;}
int mid=(l+r)>>1;
if(ll<=mid)query(l,mid,ll,rr,T[x].l);
if(rr>mid)query(mid+1,r,ll,rr,T[x].r);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(mp[a[i]])
{
update(1,n,tmp,rt[i-1],mp[a[i]],-1);
update(1,n,rt[i],tmp,i,1);
}
else update(1,n,rt[i],rt[i-1],i,1);
mp[a[i]]=i;
}
scanf("%d",&m);
while(m--)
{
ans=0;
scanf("%d%d",&l,&r);
query(1,n,l,r,rt[r]);
printf("%d\n",ans);
}
return 0;
}