1. 程式人生 > >bzoj 2724: [Violet 6]蒲公英 區間眾數 分塊

bzoj 2724: [Violet 6]蒲公英 區間眾數 分塊

Description

 求區間眾數,強制線上

         縮減了一下題意,比較清晰明瞭。

         對於該問題我們可以首先考慮分塊,對於一個連續的區間,肯定由根號n個小塊以及根號n個大塊組成,那麼答案可能是哪些呢,一定是大塊從左到右的眾數a,或者是某一個小塊的數。那大塊裡的其他數呢,可以考慮一下,既然大塊裡眾數已經是之前的a了,那麼其他的數想成為眾數,只能是加上外面小塊的數,而外面的則會被直接計算。

        接著是如何統計一個區間內一個數字的個數,最簡單的方法是大塊內排序用lower_bound logn查詢,可是這樣複雜度變成了n * 根號n  * 根號n * logn,連暴力都不如了。仔細想想,我們可以利用一下數字位置的特性,對於每一個數字開一個vector來儲存這個數字出現的每一個位置,這樣對於每個數字lower_bound查一下出現的區間即可,複雜度就成功變成了n * 根號n 

* logn。

        最後一個問題就是如何求大塊到大塊之間的眾數了,我們以每一個大塊的左端為開頭,向右一塊一塊暴力加,開一個cnt陣列輔助記錄出現次數即可。

        下附AC程式碼

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<map>
#define maxn 10005
using namespace std;
int n,q,m;
int tot;
int a[maxn];
int cnt[maxn];
int dat[maxn];
int bel[maxn];
int rea[maxn];
int dp[505][505];
map<int,int> num;
int l[maxn],r[maxn];
vector<int>edge[maxn];
void init(int now)
{
	memset(cnt,0,sizeof(cnt));
	int ans=0,maxx=0;
	for(int i=l[now];i<=n;i++)
	{
		cnt[a[i]]++;
		if(cnt[a[i]]>=ans || (cnt[a[i]]==ans && rea[a[i]]<rea[maxx]))
		{
			ans=cnt[a[i]];
			maxx=a[i];
		}
		dp[now][bel[i]]=maxx;
	}
}
int getsum(int nl,int nr,int v)
{
	int ans1=*lower_bound(edge[v].begin(),edge[v].end(),nl);
	int ans2=*lower_bound(edge[v].begin(),edge[v].end(),nr);
	if(ans2==*edge[v].end() || edge[v][ans2]>nr)
	ans2--;
	return ans2-ans1+1;
}
int query(int nl,int nr)
{
	int ans=0,maxx=0;
	if(bel[nr]-bel[nl]<=2)
	{
		for(int i=nl;i<=nr;i++)
		{
			int now=getsum(nl,nr,a[i]);
			if(now>ans || (now==ans && rea[a[i]]<rea[maxx]))
			{
				ans=now;
				maxx=a[i];
			}
		}
		return maxx;
	}
	else
	{
		for(int i=nl;i<=r[bel[nl]];i++)
		{
			int now=getsum(nl,nr,a[i]);
			if(now>ans || (now==ans && rea[a[i]]<rea[maxx]))
			{
				ans=now;
				maxx=a[i];
			}
		}
		for(int i=l[bel[nr]];i<=nr;i++)
		{
			int now=getsum(nl,nr,a[i]);
			if(now>ans || (now==ans && rea[a[i]]<rea[maxx]))
			{
				ans=now;
				maxx=a[i];
			}
		}
		int temp=dp[bel[nl]+1][bel[nr]-1];
		int now=getsum(nl,nr,temp);
		if(now>ans || (now==ans && rea[temp]<rea[maxx]))
		{
			ans=now;
			maxx=temp;
		}
		return maxx;
	}
}
int main()
{
	memset(l,0x3f,sizeof(l));
	scanf("%d%d",&n,&q);
	m=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(!num[a[i]])
		{
			num[a[i]]=++tot;
			rea[tot]=a[i];
		}
		a[i]=num[a[i]];
		edge[a[i]].push_back(i);
		bel[i]=((i-1)/m)+1;
		l[((i-1)/m+1)]=min(l[((i-1)/m+1)],i);
		r[((i-1)/m+1)]=max(r[((i-1)/m+1)],i);
	}
	for(int i=1;i<=((n-1)/m)+1;i++)
	{
		init(i);
	}
	int pre=0;
	while(q--)
	{
		int nl,nr;
		scanf("%d%d",&nl,&nr);
		nl=(nl+pre-1)%n+1;
		nr=(nr+pre-1)%n+1;
		if(nl>nr)
		swap(nl,nr);
		pre=query(nl,nr);
		printf("%d\n",rea[pre]);
	}
}