主席树在线维护区间不同数的个数
主席树在线维护区间不同数的个数
考虑一个序列\(\{a_1,a_2,a_3,...,a_n\}\),对于其中一个元素\(a_i\),假定下一个和它相同的数是\(a_j\),不难发现对于右端点大于\(j\)的区间,若这两个数对答案产生贡献,则贡献一定来自于\(a_j\)。
原因如下:
- 对于同时包含两个数的区间,我们可以认为在这些区间里两个数都对答案有贡献,但是贡献重复我们只计一个。
- 对于只包含\(a_j\)的区间,显然贡献来自于\(a_j\)
- 不难发现,由于右端点大于\(j\),所以区间内如果有\(a_i\)则必定有\(a_j\)。
综上所述,不妨直接把贡献的来源记为后一个数。
那么考虑用主席树维护这个操作。
显然对于每一个新加入的数,我们认为后面那个才是有贡献的,那么在新版本的这棵线段树中我们就可以在原先的位置\(-1\),新的位置\(+1\)。
查询区间\([L,R]\)时只需要查看\(R\)这个版本的对应区间和即可。
STL,分块,主席树:Luogu P4135 作诗
#include<cstdio>
#include<bitset>
#include<algorithm>
#include<cmath>
#define mid ((l+r)>>1)
using namespace std;
int n,c,m,nowsize,nownum,lim,l[400],r[400],num[100005],a[100005],pos[100005],rt[100005];
bitset<100005> bits1[400],bits2[400];
int tree[100005<<5],lson[100005<<5],rson[100005<<5],tot;
inline int read()
{
char c=getchar();int tmp=0;
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') tmp=tmp*10+c-48,c=getchar();
return tmp;
}
int update(int root,int l,int r,int p,int v)
{
if (p<l||r<p) return root;
int now=++tot;
tree[now]=tree[root]+v;
lson[now]=lson[root];
rson[now]=rson[root];
if (l==r) return tot;
lson[now]=update(lson[now],l,mid,p,v);
rson[now]=update(rson[now],mid+1,r,p,v);
return now;
}
int query(int root,int l,int r,int al,int ar)
{
if (ar<l||r<al) return 0;
if (al<=l&&r<=ar) return tree[root];
int ret=0;
ret+=query(lson[root],l,mid,al,ar);
ret+=query(rson[root],mid+1,r,al,ar);
return ret;
}
int main()
{
n=read(),c=read(),m=read();
nowsize=0;nownum=1;lim=sqrt(n);
l[1]=nownum;
for (int i=1;i<=n;i++)
{
a[i]=read();
if (pos[a[i]])
{
rt[i]=update(rt[i-1],1,n,pos[a[i]],-1);
pos[a[i]]=i;
rt[i]=update(rt[i],1,n,pos[a[i]],1);
}
else
{
pos[a[i]]=i;
rt[i]=update(rt[i-1],1,n,pos[a[i]],1);
}
nowsize++;
num[i]=nownum;
r[nownum]=i;
bits2[nownum][a[i]]=bits2[nownum][a[i]]^1;
if (nowsize==lim)
{
nowsize=0;
if (i+1<=n)
{
nownum++;
l[nownum]=i+1;
}
}
}
for (int i=1;i<=nownum;i++)
bits2[i]^=bits2[i-1];
int ans=0;
for (int i=1;i<=m;i++)
{
int L,R;
L=read(),R=read();
L=(L+ans)%n+1;
R=(R+ans)%n+1;
if (L>R) swap(L,R);
bitset<100005> B;
if (num[L]!=num[R])
{
for (int j=L;j<=r[num[L]];j++)
B[a[j]]=B[a[j]]^1;
B^=bits2[num[R]-1]^bits2[num[L]];
for (int j=l[num[R]];j<=R;j++)
B[a[j]]=B[a[j]]^1;
printf("%d\n",ans=query(rt[R],1,n,L,R)-B.count());
}
else
{
for (int j=L;j<=R;j++)
B[a[j]]=B[a[j]]^1;
printf("%d\n",ans=query(rt[R],1,n,L,R)-B.count());
}
}
return 0;
}