BZOJ2724 [Violet 6]蒲公英 分块

原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ2724.html

题目传送门 - BZOJ2724

题意

  求区间最小众数,强制在线。

  $n$ 个数,$m$ 次询问。

  $n\leq 40000,m\leq 50000$

题解

  看完题目:呀这不是莫队裸题吗??

  再看一遍:我去怎么是强制在线!

  然后经过一波思(forever)考(piano),终于会做了。

  首先请你自行证明一个结论:

在询问区间内任取一段子区间,询问区间内的最小众数一定是子区间的最小众数或者出现在询问区间除掉子区间的其他地方。

  于是我们考虑分块,$base=\sqrt{maxn}=200$ 一块。

  我们考虑对于所有 $i,j$ 预处理出第 $i$ 块到第 $j$ 块的区间最小众数。

  我还要预处理出每一个数在前 $i$ 块的出现次数 $\left(i\in \left[1,\left\lfloor\cfrac{n}{base}\right\rfloor\right]\right)$ 。

  于是在询问的时候只要看一看询问区间最大连续块段的最小众数和其他剩余的数就可以了。

  时间复杂度 $\Theta (n \sqrt{n})$ 。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=40005,M=205,base=200;
int n,m,hs,a[N],Ha[N],res[M][M],cnt[M][N],tax[N];
int l,r,L,R;
void HASH(){
	sort(Ha+1,Ha+n+1);
	hs=1;
	for (int i=2;i<=n;i++)
		if (Ha[i]!=Ha[i-1])
			Ha[++hs]=Ha[i];
}
int calc(int x){
	return tax[x]+cnt[r][x]-cnt[l][x];
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),Ha[i]=a[i];
	HASH();
	for (int i=1;i<=n;i++)
		a[i]=lower_bound(Ha+1,Ha+hs+1,a[i])-Ha;
	memset(cnt,0,sizeof cnt);
	for (int i=1;i<=base&&i*base<=n;i++)
		for (int j=1;j<=i*base;j++)
			cnt[i][a[j]]++;
	for (int i=1;i<=base&&i*base<=n;i++){
		memset(tax,0,sizeof tax);
		for (int j=i;j<=base&&j*base<=n;j++){
			int &Max=res[i][j];
			Max=res[i][j-1];
			for (int k=(j-1)*base+1,lim=j*base;k<=lim;k++){
				tax[a[k]]++;
				if (tax[a[k]]>tax[Max]||(tax[a[k]]==tax[Max]&&a[k]<Max))
					Max=a[k];
			}
		}
	}
	memset(tax,0,sizeof tax);
	int ans=0;
	while (m--){
		scanf("%d%d",&L,&R);
		L=(L+Ha[ans]-1)%n+1,R=(R+Ha[ans]-1)%n+1;
		if (L>R)
			swap(L,R);
		ans=0;
		if (R-L+1<=base*2){
			for (int i=L;i<=R;i++)
				tax[a[i]]++;
			for (int i=L;i<=R;i++)
				if (tax[a[i]]>tax[ans]||(tax[a[i]]==tax[ans]&&a[i]<ans))ans=a[i];
			for (int i=L;i<=R;i++)
				tax[a[i]]--;
			printf("%d\n",Ha[ans]);
			continue;
		}
		l=(L-1)/base+1,r=R/base;
		ans=res[l+1][r];
		for (int i=L;i<=l*base;i++)
			tax[a[i]]++;
		for (int i=r*base+1;i<=R;i++)
			tax[a[i]]++;
		for (int i=L;i<=l*base;i++)
			if (calc(a[i])>calc(ans)||(calc(a[i])==calc(ans)&&a[i]<ans))ans=a[i];
		for (int i=r*base+1;i<=R;i++)
			if (calc(a[i])>calc(ans)||(calc(a[i])==calc(ans)&&a[i]<ans))ans=a[i];
		for (int i=L;i<=l*base;i++)
			tax[a[i]]--;
		for (int i=r*base+1;i<=R;i++)
			tax[a[i]]--;
		printf("%d\n",Ha[ans]);
	}
	return 0;
}

  

 

posted @ 2018-07-14 07:40  zzd233  阅读(200)  评论(0编辑  收藏  举报