P4168 [Violet] 蒲公英 (莫队的强制在线)

前言

当个乐子看就行
所用时间不如分块正解快
虽然在线莫队实质也是分块

[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\)

分析

我跟正常人不一样系列

颓的时候偶然看见一种神奇的莫队

\[\color{#40c0bb}\large \textbf {莫队的强制在线} \]

知周所众 莫队是一种离线算法

而诗乃神犇将莫队进行了在线化改造  %%%诗乃

于是乎 我们就可以用在线莫队来解决这个问题啦~

根据诗乃大佬的理论

由于普通莫队是将所有询问离线排好序,然后每一次从上一个询问区间转移过来的

而想要莫队在线,我们需要预先处理好某些区间的答案,让这些区间成为莫队中的“上一个区间”,这些区间我们称为特征区间

根据诗乃大佬的证明 绝不是因为我太蒟 区间长度\(d=\sqrt{n}\)为最佳区间长度

对于特征区间需要维护的信息主要包括

1. 区间答案

2. 莫队所需要的信息(如区间中每种颜色的出现次数)

对于本题,我们设下许多特征区间,在这里的处理与分块的做法相同,不再赘述

一个区间(设其为\(L\sim R\))的众数只能是它中间夹的一堆些连续特征区间(设其为\(l\sim r\))整个的众数 \(or\) \(L\sim l\)的蒲公英的各种种类 \(or\) \(r\sim R\)的蒲公英的各种种类

所以我们不仅要预处理每个特征区间出现的种类的个数,还要处理\(i\sim j\)的众数

然后在询问时将\(l\),\(r\)(定义见上)分别移到\(L\),\(R\)即可。

由于是在线莫队,每次要清空\(cnt\)数组,需要再跳回来

记得还要将众数的出现次数清空

code

Elaina's code
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
const double eps=1e-8;
#define ll long long
//#define int long long
#define inf 0x3f
#define INF 0x3f3f3f3f
#define mst(a,b) memset(a,b,sizeof(a))
#define re register
#define Elaina 0
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

int target[N];
int nc,n,m,l=1,r=0,ans,maxx=0,num=0,col[N],a[N],anss[500][500],lsh[N];
//anss[i][j]为编号为i的特征区间到编号为j的特征区间的众数

bool vis[N];
int size,s_block;

int L[N],R[N],CNT[500][N],belong[N],cnt=0;
//CNT[i][j]表示第i个块中j种蒲公英的个数 
 
int mp[N];
map<int,int> mpp;
struct node{
	int x,y,id;
}q[N];

inline int find(int x){
	int l=0,r=n+1;
	while(l+1<r){
		int mid=(l+r)>>1;
		if(lsh[mid]<x)l=mid;
		else r=mid;
	}
	return r;
}

void init(){
	s_block=size=sqrt(n);
	for(int i=1;i<=s_block;i++){//预处理 
		L[i]=(i-1)*size+1,R[i]=i*size;
		for(int j=L[i];j<=R[i];j++){
			CNT[i][a[j]]++;
			belong[j]=i;
		}
		for(int j=1;j<=cnt;j++){//特征区间一般存前缀和 
			CNT[i][j]+=CNT[i-1][j];
		}
	}
	if(size*size<n){//若有剩余 
		s_block++;
		L[s_block]=size*size+1,R[s_block]=n;
		for(int i=size*size+1;i<=n;i++){
			CNT[s_block][a[i]]++;
			belong[i]=s_block;
		}
		for(int i=1;i<=cnt;i++){
			CNT[s_block][i]+=CNT[s_block-1][i];
		}
	}
	for(int i=1;i<=s_block;i++){//anss处理
		for(int j=i;j<=s_block;j++,anss[i][j]=anss[i][j-1]){
			for(int k=L[j];k<=R[j];k++){
				col[a[k]]++;
				if(col[a[k]]>col[anss[i][j]])anss[i][j]=a[k];
				else if(col[a[k]]==col[anss[i][j]]&&a[k]<anss[i][j])anss[i][j]=a[k];
			}
		}
		for(int j=1;j<=cnt;j++)col[j]=0;
	}
}

void add(int l,int r,int x,int k){
	if(!col[x]){//如第一次出现该种颜色,那就要加上l~r中x出现的次数
		col[x]=CNT[r][x]-CNT[l-1][x];
		vis[x]=1;
	}else if(k<0&&vis[x]){//返回时第一次出现该种颜色,就要减去l~r中x出现的次数
		col[x]-=CNT[r][x]-CNT[l-1][x];
		vis[x]=0;
	}
	col[x]+=k;
	if(k<0){
		return;
	}
	if(col[x]>col[ans]){
		ans=x;
	}else if(col[x]==col[ans]&&x<ans){
		ans=x;
	}
}
main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)lsh[i]=a[i]=read();
	sort(lsh+1,lsh+1+n);
	for(int i=2;i<=n;i++)
		if(lsh[i-1]==lsh[i])lsh[i-1]=1e9+10;
	sort(lsh+1,lsh+1+n);
	for(int i=1;i<=n;i++){//离散化
		int wz=find(a[i]);
		mp[wz]=a[i],a[i]=wz;
		cnt=max(cnt,a[i]);
	}
	
	init();
	for(int i=1;i<=m;i++){
		int Li=(read()+mp[ans]-1)%n+1,Ri=(read()+mp[ans]-1)%n+1;
		if(Li>Ri)swap(Li,Ri);
		int LL=Ri+1,RR=Ri;
		int l=Ri+1,r=Ri,posl=1,posr=0;
		if(belong[Li]+1<belong[Ri]){//中间有特征区间
			posl=belong[Li]+1,posr=belong[Ri]-1;
			ans=anss[posl][posr];
			l=LL=L[posl];
			r=RR=R[posr];
			col[ans]=CNT[posr][ans]-CNT[posl-1][ans];
		}
        //莫队小四只
		while(l>Li)add(posl,posr,a[--l],1);
		while(r<Ri)add(posl,posr,a[++r],1);
		printf("%d\n",mp[ans]);
		while(l<LL)add(posl,posr,a[l++],-1);
		while(r>RR)add(posl,posr,a[r--],-1);
		if(belong[Li]+1<belong[Ri]){//减去中间夹的特征区间的众数个数
			col[anss[posl][posr]]-=CNT[posr][anss[posl][posr]]-CNT[posl-1][anss[posl][posr]];
	    }
    }

	return Elaina;
}
/*
6 3 
1 2 3 2 1 2 
1 5 
3 6 
1 5 


10 0
1 2 3 4 5 6 9 9 9 9
*/

都看到这了,真的不点个赞吗(>ω<*)

posted @ 2024-04-22 22:32  Elaina_0  阅读(31)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end