BZOJ2821作诗(Poetize) 分块

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2821作诗(Poetize) 分块相关的知识,希望对你有一定的参考价值。

【BZOJ2821】作诗(Poetize)

Description

神犇SJY虐完HEOI之后给傻×LYD出了一题:SHY是T国的公主,平时的一大爱好是作诗。由于时间紧迫,SHY作完诗
之后还要虐OI,于是SHY找来一篇长度为N的文章,阅读M次,每次只阅读其中连续的一段[l,r],从这一段中选出一
些汉字构成诗。因为SHY喜欢对偶,所以SHY规定最后选出的每个汉字都必须在[l,r]里出现了正偶数次。而且SHY认
为选出的汉字的种类数(两个一样的汉字称为同一种)越多越好(为了拿到更多的素材!)。于是SHY请LYD安排选
法。LYD这种傻×当然不会了,于是向你请教……问题简述:N个数,M组询问,每次问[l,r]中有多少个数出现正偶
数次。

Input

输入第一行三个整数n、c以及m。表示文章字数、汉字的种类数、要选择M次。第二行有n个整数,每个数Ai在[1, c
]间,代表一个编码为Ai的汉字。接下来m行每行两个整数l和r,设上一个询问的答案为ans(第一个询问时ans=0),
令L=(l+ans)mod n+1, R=(r+ans)mod n+1,若L>R,交换L和R,则本次询问为[L,R]。

Output

输出共m行,每行一个整数,第i个数表示SHY第i次能选出的汉字的最多种类数。

Sample Input

5 3 5
1 2 2 3 1
0 4
1 2
2 2
2 3
3 5

Sample Output

2
0
0
0
1

HINT

对于100%的数据,1<=n,c,m<=10^5

题解:分块大法太强了~

我们先分块,对于每一个询问[l,r]我们都可以将它拆成中间一堆大块和两边的小块,设f[i][j]表示从第i个块到第j个块的答案,我们先枚举每个块,暴力扫所有i块后面的点,这样就能边扫边预处理出f[i][j],到时候我们就直接调用就好了,现在我们处理两边的小块

我们将两边小块里的元素暴力扫一遍,然后排个序,这样我们就能得到每个汉字出现的次数了,这时我们就需要提前预处理出一个g[i][j],表示在1~i的块里j出现的个数,然后我们就能得出每个汉字的总个数了

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n,m,c,siz,ans,num;
int f[320][320],g[320][100010],v[100010],s[100010],p[640];
int main()
{
	scanf("%d%d%d",&n,&c,&m);
	int i,j,a,b;
	siz=int(sqrt(1.0*n));
	for(i=0;i<n;i++)	scanf("%d",&v[i]),g[i/siz][v[i]]++;
	for(i=0;i<=n/siz;i++)
	{
		if(i)	for(j=1;j<=c;j++)	g[i][j]+=g[i-1][j];
		memset(s,0,sizeof(s));
		for(j=i*siz;j<n;j++)
		{
			s[v[j]]++;
			if(s[v[j]]==2)	f[i][j/siz]++;
			if(s[v[j]]==3)	f[i][j/siz]--,s[v[j]]=1;
			if(j&&j%siz==0)	f[i][j/siz]+=f[i][j/siz-1];
		}
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		a=(a+ans)%n,b=(b+ans)%n;
		num=0;
		if(a>b)	swap(a,b);
		if(a/siz==b/siz)
		{
			ans=0;
			for(j=a;j<=b;j++)	p[num++]=v[j];
			sort(p,p+num);
			int pre=0;
			for(j=0;j<num;j++)
			{
				if(j&&p[j]!=p[j-1])
				{
					ans+=(pre%2==0)?1:0;
					pre=0;
				}
				pre++;
			}
			ans+=(pre%2==0)?1:0;
		}
		else
		{
			ans=f[a/siz+1][b/siz-1];
			for(j=a;j<(a/siz+1)*siz;j++)	p[num++]=v[j];
			for(j=b/siz*siz;j<=b;j++)	p[num++]=v[j];
			sort(p,p+num);
			int pre=0;
			for(j=0;j<num;j++)
			{
				if(j&&p[j]!=p[j-1])
				{
					if(g[b/siz-1][p[j-1]]>g[a/siz][p[j-1]]&&((g[b/siz-1][p[j-1]]-g[a/siz][p[j-1]])%2==0))	ans--;
					if((pre+g[b/siz-1][p[j-1]]-g[a/siz][p[j-1]])%2==0)	ans++;
					pre=0;
				}
				pre++;
			}
			if(g[b/siz-1][p[num-1]]>g[a/siz][p[num-1]]&&((g[b/siz-1][p[num-1]]-g[a/siz][p[num-1]])%2==0))	ans--;
			if((pre+g[b/siz-1][p[num-1]]-g[a/siz][p[num-1]])%2==0)	ans++;
		}
		printf("%d\n",ans);
	}
	return 0;
}

以上是关于BZOJ2821作诗(Poetize) 分块的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2821: 作诗(Poetize) | 分块

BZOJ 2821作诗(Poetize) 分块

bzoj2821 作诗(Poetize)

BZOJ2821: 作诗(Poetize)

[BZOJ2821]作诗(Poetize)

[BZOJ2821]作诗(Poetize)