BZOJ 2724: [Violet 6]蒲公英

题目大意:

求区间众数,强制在线。

题解:

考虑分块,一段区间的众数一定在整块的众数和两边多出来的数中。

可能是众数的数有O(sqrt(n))个,然后我们考虑查询这些数在区间中出现了几次。

把原来的序列中相同数字的下标从小到大扔进vector中,然后二分就能求每个数在区间中出现了几次。

找一个出现次数最大的就行了。

复杂度O(nsqrt(n)log(n))

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
int id,n,m,block,ans,f[505][505],cnt[100005],v[100005],belong[1000005],val[1000005];
map<int,int> mp;
vector<int> vec[1000005];
void pre(int x){
	memset(cnt,0,sizeof(cnt));
	int maxx=0,ans=0;
	for (int i=(x-1)*block+1; i<=n; i++){
		cnt[v[i]]++;
		int t=belong[i];
		if (cnt[v[i]]>maxx || (cnt[v[i]]==maxx && val[v[i]]<val[ans])){
			ans=v[i];
			maxx=cnt[v[i]];
		}
		f[x][t]=ans;
	}
}
int query(int l,int r,int x){
	int t=upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l);
	return t;
}
int calc(int l,int r){
	int ans=f[belong[l]+1][belong[r]-1];
	int maxx=query(l,r,ans);
	for (int i=l; i<=min(belong[l]*block,r); i++){
		int x=query(l,r,v[i]);
		if (x>maxx || (x==maxx && val[v[i]]<val[ans])) {
			ans=v[i];
			maxx=x;
		}
	}
	if (belong[l]!=belong[r]){
		for (int i=(belong[r]-1)*block+1; i<=r; i++){
			int x=query(l,r,v[i]);
			if (x>maxx || (x==maxx && val[v[i]]<val[ans])) {
				ans=v[i];
				maxx=x;
			}
		}
	}
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	block=200;
	int ans=0;
	for (int i=1; i<=n; i++){
		scanf("%d",&v[i]);
		if (!mp[v[i]]){
			mp[v[i]]=++id;
			val[id]=v[i];
		}
		v[i]=mp[v[i]];
		vec[v[i]].push_back(i);
	}
	for (int i=1; i<=n; i++)
		belong[i]=(i-1)/block+1;
	int cnt=(n-1)/block+1;
	for (int i=1; i<=cnt; i++) pre(i);
	for (int i=1; i<=m; i++){
		int l,r;
		scanf("%d%d",&l,&r);
		l=(l+ans-1)%n+1,r=(r+ans-1)%n+1;
		if (l>r) swap(l,r);
		ans=val[calc(l,r)];
		printf("%d\n",ans);
	}
	return 0;
}

  

 

posted @ 2018-04-18 20:21  ~Silent  阅读(118)  评论(0编辑  收藏  举报
Live2D