主席树在线维护区间不同数的个数

主席树在线维护区间不同数的个数

考虑一个序列\(\{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;
}
posted @ 2020-11-25 15:55  Nanjo  阅读(87)  评论(0编辑  收藏  举报