蒲公英

[Violet] 蒲公英

题目背景

亲爱的哥哥:

你在那个城市里面过得好吗?

我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受的时候才做出这样的事的……

最近村子里长出了一大片一大片的蒲公英。一刮风,这些蒲公英就能飘到好远的地方了呢。我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢!

哥哥你要快点回来哦!

爱你的妹妹 Violet

Azure 读完这封信之后微笑了一下。

“蒲公英吗……”

题目描述

在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为 \(n\) 的序列 \(\{a_1,a_2..a_n\}\),其中 \(a_i\) 为一个正整数,表示第 \(i\) 棵蒲公英的种类编号。

而每次询问一个区间 \([l, r]\),你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

注意,你的算法必须是在线的

输入格式

第一行有两个整数,分别表示蒲公英的数量 \(n\) 和询问次数 \(m\)

第二行有 \(n\) 个整数,第 \(i\) 个整数表示第 \(i\) 棵蒲公英的种类 \(a_i\)

接下来 \(m\) 行,每行两个整数 \(l_0, r_0\),表示一次询问。输入是加密的,解密方法如下:

令上次询问的结果为 \(x\)(如果这是第一次询问,则 \(x = 0\)),设 \(l=((l_0+x-1)\bmod n) + 1,r=((r_0+x-1) \bmod n) + 1\)。如果 \(l > r\),则交换 \(l, r\)
最终的询问区间为计算后的 \([l, r]\)

输出格式

对于每次询问,输出一行一个整数表示答案。

样例 #1

样例输入 #1

6 3 
1 2 3 2 1 2 
1 5 
3 6 
1 5

样例输出 #1

1 
2 
1

提示

数据规模与约定

  • 对于 \(20\%\) 的数据,保证 \(n,m \le 3000\)
  • 对于 \(100\%\) 的数据,保证 \(1\le n \le 40000\)\(1\le m \le 50000\)\(1\le a_i \le 10^9\)\(1 \leq l_0, r_0 \leq n\)

题解

(开始接受洛谷自带Markdown)

分块好题! 求众数,由于众数不具有区间结合律(众数无法由几个众数求出),因此考虑分块暴力。

离散化后开始桶装暴力,预处理。

查询比较好想,先零散块,再整块。

  1. 预处理出每两个块间的众数,然后整个区间的众数只可能是中间整块的众数或是边缘零散块的数。

    遍历每一个块作为起点,往后找每一个数出现次数,并且把这个数所在块作为终点,更新。

  2. 维护用 \(vector\) 存每一个数出现的位置,用 \(upper_bound\)\(lower_bound\) 找出下标做差,即为出现次数。

    注意这里是 \(upper_bound\) ,减出来刚好是区间长度。如求 \(x\)\([l,r]\)的出现次数,

    正常写的时候应该是 \((r-l+1)\) ,如果第 \(r\) 位置正好有 \(x\),那么会找到 \((r+1)\) ,

    如果没有会找到比他大的那个,这时最后一个小于 \(x\) 的下标为 \(r\),则找到 \((r+1)\)

code
#include<bits/stdc++.h>
using namespace std;
const int N = 40005,M = 300;
int n,m,tot;
int a[N],L[M],R[M],bl[N],sum[M][M];
int t[M][N];
unordered_map<int,int> mp;
int f[N];
vector<int> v[N];
void bui()
{
	int cnt=sqrt(n);
	for(int i=1;i<=cnt;i++)
	{
		L[i]=n/cnt*(i-1)+1;
		R[i]=n/cnt*i;
	}
	R[cnt]=n;
	for(int i=1;i<=cnt;i++)
		for(int j=L[i];j<=R[i];j++)
			bl[j]=i;
	
	for(int i=1;i<=cnt;i++)
	{
		int tmp=0,res=0;
		for(int j=L[i];j<=n;j++)
		{
			t[i][a[j]]++;
			if(t[i][a[j]]>tmp||(t[i][a[j]]==tmp&&f[res]>f[a[j]]))
				tmp=t[i][a[j]], res=a[j];
			sum[i][bl[j]]=res;
		}
	}
}
int Q(int l,int r,int x)
{
	return (upper_bound(v[x].begin(),v[x].end(),r)-lower_bound(v[x].begin(),v[x].end(),l));
}
int que(int l,int r)
{
	int res=sum[bl[l]+1][bl[r]-1];
	int tmp=Q(l,r,res);
	for(int i=l;i<=min(r,R[bl[l]]);i++)
	{
		int t=Q(l,r,a[i]);
		if(t>tmp||(t==tmp&&f[res]>f[a[i]]))
		res=a[i],tmp=t;
	}
	if(bl[l]!=bl[r])	
	{
		for(int i=L[bl[r]];i<=r;i++)
		{
			int t=Q(l,r,a[i]);
			if(t>tmp||(t==tmp&&f[res]>f[a[i]]))
			res=a[i],tmp=t;			
		}
	}
	return f[res];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(mp.find(a[i])==mp.end())
		{	
			mp[a[i]]=++tot; f[tot]=a[i];
		}
		a[i]=mp[a[i]];
		v[a[i]].push_back(i);
	}
	bui();
	int x=0;
	while(m--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		l=((l+x-1)%n)+1; r=((r+x-1)%n)+1;
		if(l>r) swap(l,r);
		x=que(l,r);
		printf("%d\n",x);
	}
	return 0;
}

注意

分块求前缀和或类似的区间值很方便,可以以块为单位记录。注意优化。

posted @ 2024-04-24 11:04  ppllxx_9G  阅读(16)  评论(0编辑  收藏  举报