Luogu P4168 [Violet]蒲公英

Luogu P4168(分块)

Luogu P4168

看到数据范围 \(4e4\) 就基本可以猜到是分块做的题,而这道题要求能够查询 \([l,r]\) 的众数,这一点不满足区间可加性,因此,不能使用线段树和树状数组来进行维护,所以可以想到分块。

因为 \(a_i\in [1,10^9]\) ,所以用桶统计 \(a_i\) 的值时需要离散化。

将区间拆分成为 \(\sqrt{n}\) 个区块,因此每个区块中的元素个数为 \(\sqrt{n}\)

首先我们需要维护 \(s\)\(f\) 两个数组, \(s_{i,j}\) 表示离散值 \(j\) 在前 \(i\) 个区块出现的总次数,直接暴力维护,时间复杂度为 \(O(n\sqrt{n})\)\(f_{i,j}\) 表示区块 \(i\) 至区块 \(j\) 之间(包括 \(i\)\(j\) )的众数的离散值,枚举 \(i,j\) ,再在区块 \(j\) 中枚举元素位置 \(k\) 进行处理,时间复杂度也为 \(O(n\sqrt{n})\)

对于每一次查询,如果 \(l\)\(r\) 处于相同或相邻区块,那么直接在 \([l,r]\) 之间暴力统计即可,时间复杂度为 \(O(\sqrt{n})\) (因为区间长度为 \(\sqrt{n}\) );如果 \(l\)\(r\) 不位于相同或相邻区块,那么将查询区间分为三部分:左侧非整区块,中间整区块,右侧非整区块,对于左右非整区块直接暴力统计每个值出现次数,而对于中间的整区块,直接使用开始时提前处理好的 \(s\)\(f\) 即可,时间复杂度也为 \(O(n\sqrt{n})\)

注意在数据离散之后使用离散后的数据,分清楚此时的数据是否已经离散化,避免因非数据结构本身问题导致的过长调试时间。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
int read()
{
	int k=0,flag=1;char b=getchar();
	while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
	while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
	return k*flag;
}
const int _SIZE=4e4;
int n,m;
int len,sum,cnt;
int a[_SIZE+5],b[_SIZE+5];
int val[_SIZE+5],pos[_SIZE+5];
int s[205][_SIZE+5],f[205][205],t[_SIZE+5];
struct BLOCK{
	int l,r;
}block[205];
void PreWork()
{
	len=sqrt(n);
	sort(b+1,b+n+1);
	sum=unique(b+1,b+n+1)-b-1;
	cnt=(n-1)/len+1;
	for (int i=1;i<=n;i++)
		val[i]=lower_bound(b+1,b+sum+1,a[i])-b;
	for (int i=1;i<=cnt;i++)
	{
		block[i].l=(i-1)*len+1;
		block[i].r=min(n,i*len);
	}
	for (int i=1;i<=cnt;i++)
	{
		for (int j=block[i].l;j<=block[i].r;j++)
			s[i][val[j]]++,pos[j]=i;
		for (int j=1;j<=sum;j++)
			s[i][j]+=s[i-1][j];
	}
	for (int i=1;i<=cnt;i++)
		for (int j=i;j<=cnt;j++)
		{
			int most=f[i][j-1];
			for (int k=block[j].l;k<=block[j].r;k++)
			{
				int appear=s[j][val[k]]-s[i-1][val[k]],mostAppear=s[j][most]-s[i-1][most];
				//printf("%d~%d %d:%d\n",block[i].l,block[j].r,b[val[k]],appear);
				if (appear>mostAppear || (appear==mostAppear && val[k]<most))
					most=val[k];
			}
			f[i][j]=most;
		}
}
int query(int l,int r)
{
	int most=0;
	if (l>r) swap(l,r);
	if (pos[r]-pos[l]<=1)
	{
		for (int i=l;i<=r;i++)
			t[val[i]]++;
		for (int i=l;i<=r;i++)
			if (t[val[i]]>t[most] || (t[val[i]]==t[most] && val[i]<most))
				most=val[i];
	}
	else
	{
		for (int i=l;i<=block[pos[l]].r;i++)
			t[val[i]]++;
		for (int i=block[pos[r]].l;i<=r;i++)
			t[val[i]]++;
		most=f[pos[l]+1][pos[r]-1];
		for (int i=l;i<=block[pos[l]].r;i++)
		{
			int mostAppear=s[pos[r]-1][most]-s[pos[l]][most]+t[most];
			int currAppear=s[pos[r]-1][val[i]]-s[pos[l]][val[i]]+t[val[i]];
			if (currAppear>mostAppear || (currAppear==mostAppear && val[i]<most))
				most=val[i];
		}
		for (int i=block[pos[r]].l;i<=r;i++)
		{
			int mostAppear=s[pos[r]-1][most]-s[pos[l]][most]+t[most];
			int currAppear=s[pos[r]-1][val[i]]-s[pos[l]][val[i]]+t[val[i]];
			if (currAppear>mostAppear || (currAppear==mostAppear && val[i]<most))
				most=val[i];
		}
	}
	mem(t,0);
	return b[most];
}
int main()
{
	//freopen("P4168.in","r",stdin);
	n=read(),m=read();
	for (int i=1;i<=n;i++) a[i]=b[i]=read();
	PreWork();
	//for (int i=1;i<=cnt;i++) {printf("1~%d ",block[i].r);for (int j=1;j<=sum;j++){printf("%d:%d | ",b[j],s[i][j]);}puts("");}
	//for (int i=1;i<=cnt;i++) printf("%d~%d %d\n",block[i].l,block[i].r,b[f[i][i]]);
	int x=0;
	while (m--)
	{
		int l=read(),r=read();
		l=(l+x-1)%n+1,r=(r+x-1)%n+1;
		x=query(l,r);
		printf("%d\n",x);
	}
	return 0;
}

posted @ 2022-07-06 09:28  Hanx16Msgr  阅读(5)  评论(0编辑  收藏  举报