题目背景
在那遥远的西南有一所学校
/被和谐部分/
然后去参加该省省选虐场
然后某蒟蒻不会做,所以也出了一个字符串题:
题目描述
给你一个字符串a,每次询问一段区间的贡献
贡献定义:
每次从这个区间中随机拿出一个字符x,然后把x从这个区间中删除,你要维护一个集合S
如果S为空,你rp减1
如果S中有一个元素不小于x,则你rp减1,清空S
之后将x插入S
由于你是大爷,平时做过的题考试都会考到,所以每次询问你搞完这段区间的字符之后最多还有多少rp?rp初始为0
询问之间不互相影响~
输入格式
第一行两个数n,m,表示字符串长度与询问次数
之后一行n个数,表示字符串
由于你是大爷,所以字符集1e9
之后m行每行两个数,表示询问的左右区间
输出格式
m行,每行一个数表示答案
输入输出样例
输入 #1 复制
3 3
3 3 3
3 3
3 3
3 3
输出 #1 复制
-1
-1
-1
说明/提示
前4个点1s,后面的点4s
对于10%的数据,是样例
对于另外10%的数据,n,m <= 100
对于另外10%的数据,n,m <= 1000
对于另外10%的数据,n,m <= 10000
对于另外10%的数据,n,m <= 100000
对于100%的数据,n,m <= 200000
保证数据向某省省选day1T2一样sb,大家尽情用暴力水过题吧!
没事,你只要在一个好学校,就算这题只能拿到10分,也可以进队了
题目很难读懂,大概就是问你一个区间众数出现的次数,然后加个负号。为什么呢?很容易看出,我们减少的rp,就是递增序列的个数,然后就能转化为众数问题了,因为如果两个数字不相同,那么一定能存在于同一递增序列当中,反之不存在同一递增序列,那么两数相等。 故题目就是求众数出现的次数。
这题可以离线,那么我们直接莫队就好了(分块二分太麻烦)。
我们维护每个数字出现的次数 cnt ,和出现当前次数的一共有多少个 num。一个记录当前的答案sum。
然后我们就可以区间转移了。
add:让x的出现次数增加 cnt[x]++,num[cnt[x]]++ , num[cnt[x]-1]-- ; 然后看当前的cnt[x]与当前的sum,取最大值。
del:和上面差不多,只是判断答案时,需要判断当前的cnt[x]减少之后会不会改变sum,怎么判断呢?如果当前的sum[cnt[x]]只有一个,那么减少后,答案肯定减一,否则不变。
还要注意,需要离散化一下。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10;
int n,m,len,bl,a[N],b[N],cnt[N],num[N],sum,res[N],cl,cr;
struct node{
int l,r,id;
}t[N];
int cmp(const node &s1,const node &s2){
return (s1.l/bl==s2.l/bl)?(s1.r<s2.r):(s1.l<s2.l);
}
inline void add(int x){
num[cnt[a[x]]++]--; num[cnt[a[x]]]++; sum=max(sum,cnt[a[x]]);
}
inline void del(int x){
num[cnt[a[x]]]--; if(cnt[a[x]]==sum&&!num[sum]) sum--; num[--cnt[a[x]]]++;
}
signed main(){
scanf("%d %d",&n,&m); bl=sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
for(int i=1;i<=m;i++) scanf("%d %d",&t[i].l,&t[i].r),t[i].id=i;
sort(b+1,b+1+n); 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;
sort(t+1,t+1+m,cmp); cl=1;
for(int i=1;i<=m;i++){
int L=t[i].l; int R=t[i].r;
while(cl<L) del(cl++);
while(cl>L) add(--cl);
while(cr<R) add(++cr);
while(cr>R) del(cr--);
res[t[i].id]=sum;
}
for(int i=1;i<=m;i++) printf("%d\n",-res[i]);
return 0;
}