让我们吐槽一下,该题的题面不光要求题意转化功底要好,还同样要求你的语文功底要好。
给出原题面:
其实也算不上理解不了,不过出的这样毒瘤的题面但本质却很简单,只能说出题人很会玩。
让我们逐步刨析:
1.这一操作有且仅有一次,因此实质就是固定的常数1。
2.由于我们需要让rp尽可能的大,也就是减的尽可能少,因此这一操作是重点要维护的。
3.x插入S,因此S除了刚开始,永远都不会为空,故与上述的操作一对应。
为了让操作二减的尽可能少,显然要尽可能的让S中的数呈严格上升子序列。
通过接下来的模拟帮助大家进一步转化:
假如要查询的区间中的数字为 1 2 2 3 3 3 或者 1 2 2 2 3 3
那么我们刚开始要添加的x一定是最小的,然后下一次再选比x大的数,这样便能满足严格上升。
直到没有比x更大的数再停止,使rp--。
模拟第一组:
一开始为空 rp--
(1)S:[1,2,3] 剩:2 3 3
(2)rp-- S:[2,3] 剩:3
(3)rp-- S:[3] 结束
最终rp = -3
模拟第二组:
一开始为空 rp--
(1)S:[1,2,3] 剩:2 2 3
(2)rp-- S:[2,3] 剩:2
(3)rp-- S:[2] 结束
最终rp = -3
通过以上的模拟,我们可以得出什么?
好像rp值减去的数等于查询区间中的众数诶?
真的吗?让我们思考一下正确性。
每次配对的数字一定是从小到大的。
并且只要是不同的数,都可以被加入到S中。
完成上述操作之后,一定不能再加入数字了,因此清空S,rp--,把新的x加入S。
可以看出,每一次上述操作后,不同数在区间中剩下来的数量都会-1。
而当这一区间中所有数的数量为0才会结束。
因此rp--的次数不就是该查询区间的众数吗?
思路得出,实践开始。
离线查询考虑莫队,看到a[i]的取值范围如此之大,因此统计出现次数的数组可能会爆。
考虑离散化莫队,按照上述思路再开一个数组t。
莫队核心思路:
cnt[i]表示数i出现的次数,t[i]表示出现i次的数有多少个。
加入一个数时,把now和cnt取个最大值。
删除一个数时,如果有t[cnt]==1 && cnt==now那么now−−。
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,m;
int a[N],cnt[N],ans[N],t[N],b[N];
struct query{
int l,r,b,id;
}e[N];
bool cmp(query a,query b){
return (a.b^b.b) ? a.b<b.b : ((a.b&1) ? a.r<b.r : a.r>b.r);
}
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x<0) putchar('-'),x=-x;if(x>9) write(x/10);putchar(x%10+'0');return;}
signed main(){
n=read(),m=read();
int size=sqrt(n);
for(int i=1;i<=n;i++){
b[i]=a[i]=read();
}
sort(b+1,b+1+n);
int len=unique(b+1,b+1+n)-b-1;//返回去重后的末尾元素的下一个位置
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+len+1,a[i])-b;
}
for(int i=1;i<=m;i++){
e[i].l=read(),e[i].r=read();
e[i].b=e[i].l/size;
e[i].id=i;
}
int l=1,r=0,now=0;
sort(e+1,e+1+m,cmp);
for(int i=1;i<=m;i++){
int x=e[i].l,y=e[i].r;
while(l<x){
t[cnt[a[l]]]--;
if(!t[cnt[a[l]]] && cnt[a[l]]==now) now--;
t[--cnt[a[l]]]++;
l++;
}
while(l>x){
--l;
t[cnt[a[l]]]--;
t[++cnt[a[l]]]++;
now=max(cnt[a[l]],now);
}
while(r<y){
++r;
t[cnt[a[r]]]--;
t[++cnt[a[r]]]++;
now=max(cnt[a[r]],now);
}
while(r>y){
t[cnt[a[r]]]--;
if(!t[cnt[a[r]]] && cnt[a[r]]==now) now--;
t[--cnt[a[r]]]++;
r--;
}
ans[e[i].id]=-now;
}
for(int i=1;i<=m;i++){
write(ans[i]);puts("");
}
return 0;
}
双倍经验:P1997,不需要离散化,注意a[i]可以取负数。