前言
当个乐子看就行
所用时间不如分块正解快
虽然在线莫队实质也是分块
[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\)。
分析
我跟正常人不一样系列
颓的时候偶然看见一种神奇的莫队
知周所众 莫队是一种离线算法
而诗乃神犇将莫队进行了在线化改造 %%%诗乃
于是乎 我们就可以用在线莫队来解决这个问题啦~
根据诗乃大佬的理论
由于普通莫队是将所有询问离线排好序,然后每一次从上一个询问区间转移过来的
而想要莫队在线,我们需要预先处理好某些区间的答案,让这些区间成为莫队中的“上一个区间”,这些区间我们称为特征区间
根据诗乃大佬的证明 区间长度\(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
*/
都看到这了,真的不点个赞吗(>ω<*)