码迷,mamicode.com
首页 > 其他好文 > 详细

P4168 [Violet]蒲公英 题解

时间:2020-11-01 09:29:32      阅读:16      评论:0      收藏:0      [点我收藏+]

标签:std   pen   etc   ret   ||   级别   更新   区间   个数   

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(N\sqrt{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\n",last);
	}
	return 0;
}

P4168 [Violet]蒲公英 题解

标签:std   pen   etc   ret   ||   级别   更新   区间   个数   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!