P4168 [Violet]蒲公英 题解

Link

P4168 [Violet]蒲公英

Solve

本题是非常经典的在线求区间众数问题。因为众数不具有区间可加性(已知([x,y])([y+1,z])的众数,不能直接得到([x,z])的众数),所以用树状数组或者线段树维护就特别难,我们考虑分块来做。

我们把序列分成(T)块,每块长度(L=N/T)

对于每个询问([l,r]),设(l)处于第(p)块,(r)处于第(q)块,我们可以把([l,r])分成三个部分。

1.开头不足一段([l,L))

2.第(p+1)~(q-1)块的区间([L,R])

3.结尾不足一段((L,R])

显然,序列在区间([l,r])上的众数只可能来自于以下两种情况

1.区间([L,R])的众数

2.出现在([l,L))和((R,r])之间的数

我们先预处理出义"段边界"为端点的区间([L,R])的众数,然后开(N)(vextor)保存数值(a_i)所在的位置(离散后)。

对于每个询问扫描([l,L))((R,r])中的每个数(x),在对应的(vector)中二分查找即可得到(x)([l,r])中出现的次数,从而更新答案,

这个算法的时间为(O(NT+NM/T ast log N)),空间为(O(N^2))。应取(T=sqrt{NlogN}),使得整个算法时间复杂度在(O(Nsqrt{NlogN}))级别。

代码细节比较多,调了一个早上

#include<cstdio>
#include<vector>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
const int maxn=40005,maxt=205;
int N,t,M,p[maxt][maxt],m,L[maxt],R[maxt],pos[maxn],cnt[maxn],last,up[maxn];
vector <int> Q[maxn];
struct AS{
	int x,id,color;
}a[maxn];
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
bool cmp1(AS A,AS B){return A.x<B.x;}
bool cmp2(AS A,AS B){return A.id<B.id;}
int get(int l,int r,int x){
	return upper_bound(Q[x].begin(),Q[x].end(),r)-lower_bound(Q[x].begin(),Q[x].end(),l);
}
int query(int l,int r){
	int max_x=0,num=0,res;
	if(pos[l]+1<pos[r]){max_x=get(l,r,p[pos[l]+1][pos[r]-1]),num=p[pos[l]+1][pos[r]-1];}
	for(int i=l;i<=R[pos[l]];i++){
		res=get(l,r,a[i].color);
		if(res>max_x||(res==max_x&&a[i].color<num)){max_x=res;num=a[i].color;}
	}
	if(pos[l]!=pos[r])
		for(int i=L[pos[r]];i<=r;i++){
			res=get(l,r,a[i].color);
			if(res>max_x||(res==max_x&&a[i].color<num)){max_x=res;num=a[i].color;}
		}
	return num;
}
int main(){
	freopen("P4168.in","r",stdin);
	freopen("P4168.out","w",stdout);
	N=read();M=read();
	for(int i=1;i<=N;i++)a[i].x=read(),a[i].id=i;
	sort(a+1,a+1+N,cmp1);
	for(int i=1;i<=N;i++){
		if(a[i].x!=a[i-1].x)++m,up[m]=a[i].x;
		a[i].color=m;
	}
	sort(a+1,a+1+N,cmp2);
	for(int i=1;i<=N;i++)Q[a[i].color].push_back(i);
	for(int i=1;i<=m;i++)Q[i].push_back(N+1);
	t=sqrt(N);
	for(int i=1;i<=t;i++){
		L[i]=(i-1)*sqrt(N)+1;
		R[i]=i*sqrt(N);
	}
	if(R[t]<N){t++;L[t]=R[t-1]+1;R[t]=N;}
	for(int i=1;i<=t;i++){
		for(int j=L[i];j<=R[i];j++){pos[j]=i;}
	}
	for(int i=1;i<=t;i++){
		memset(cnt,0,sizeof cnt);int max_x=0,num=0;
		for(int j=L[i];j<=N;j++){
			cnt[a[j].color]++;
			if(cnt[a[j].color]>max_x||(cnt[a[j].color]==max_x&&a[j].color<num)){
				max_x=cnt[a[j].color];num=a[j].color;
			}
			p[i][pos[j]]=num;
		}
	}
	while(M--){
		int l=read(),r=read();
		l=(l+last-1)%N+1,r=(r+last-1)%N+1;
		if(l>r)swap(l,r);
		last=up[query(l,r)];
		printf("%d
",last);
	}
	return 0;
}

原文地址:https://www.cnblogs.com/martian148/p/13884192.html