题目描述
给你一个字符串a,每次询问一段区间的贡献
贡献定义:
每次从这个区间中随机拿出一个字符x,然后把x从这个区间中删除,你要维护一个集合S
如果S为空,你rp减1
如果S中有一个元素不小于x,则你rp减1,清空S
之后将x插入S
由于你是大爷,平时做过的题考试都会考到,所以每次询问你搞完这段区间的字符之后最多还有多少rp?rp初始为0
询问之间不互相影响~
输入输出格式
输入格式:
第一行两个数n,m,表示字符串长度与询问次数
之后一行n个数,表示字符串
由于你是大爷,所以字符集1e9
之后m行每行两个数,表示询问的左右区间
输出格式:
m行,每行一个数表示答案
输入输出样例
输出样例#1:
复制
View Code
-1 -1 -1
借着此题复习一下莫队
首先分析一下题目:
要求减去的RP最少,那么就是求最少可以将数列分成几段单调上升子序列。
单调上升子序列的个数,就是最后的答案。
如果所有数字均不重复,那么答案等于1
有一个数字重复,那么就是2
因此,答案就等于众数的出现次数。
因为ai最大为109,所以要先对a数组离散化。(因为只是求众数出现次数,故离散化后无任何影响)
统计一个数字出现次数cnt,再统计一下这个数字出现次数的出现次数(形容的比较抽象,看下面的解释/代码)。
- add操作
只要这个数字出现次数等于当前ans,那么ans++。
- del操作
当前数字出现次数等于ans,并且这个数字的数量的出现次数为1,即没有别的数字出现次数等于ans,那么ans--
代码如下:
#include <bits/stdc++.h> using namespace std; inline void read(int &a) {a=0;int b=1,c=getchar(); while(c!='-'&&(c>'9'||c<'0')) c=getchar(); if(c=='-') b=-1,c=getchar(); while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar(); a*=b; } const int N=2e5+5; int n,m,a[N],block,b[N],cnt[N],rec[N],ans,zs[N]; struct sq { int l,r,id; }q[N]; inline bool cmp(sq a,sq b) { return (a.r/block)==(b.r/block)?a.l<b.l:a.r<b.r; } inline void add(int x) { if(cnt[a[x]]==ans) ++ans; zs[cnt[a[x]]]--; zs[++cnt[a[x]]]++; } inline void del(int x) { if(ans==cnt[a[x]]&&zs[cnt[a[x]]]==1) --ans; zs[cnt[a[x]]]--; zs[--cnt[a[x]]]++; } int main() { read(n);read(m);block=sqrt(n); for(int i=1;i<=n;i++) { read(a[i]); b[i]=a[i]; } 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+1+len,a[i])-b; for(int i=1;i<=m;i++) {read(q[i].l);read(q[i].r); q[i].id=i; } sort(q+1,q+1+m,cmp); int l=1,r=0; for(int i=1,ql,qr,qi;i<=m;i++) { ql=q[i].l,qr=q[i].r,qi=q[i].id; while(l<ql) del(l++); while(l>ql) add(--l); while(r<qr) add(++r); while(r>qr) del(r--); rec[qi]=ans; } for(int i=1;i<=m;i++) cout<<-rec[i]<<'\n'; return 0; }