题目
给你一个字符串a,每次询问一段区间的贡献
贡献定义:
每次从这个区间中随机拿出一个字符x,然后把x从这个区间中删除,你要维护一个集合S
如果S为空,你rp减1
如果S中有一个元素不小于x,则你rp减1,清空S
之后将x插入S
由于你是大爷,平时做过的题考试都会考到,所以每次询问你搞完这段区间的字符之后最多还有多少rp?rp初始为0
询问之间不互相影响~
分析
语文考察题。。。
我的语文水平真的是没救了
仔细分析题目后就会发现,其实题目是在求一个区间中的众数:
本质是每次从区间中取出一个严格上升的序列,然后问最少取几次。由于是严格上升,所以只和相同的数个数有关,即要用区间出现次数最大的那个数出现次数那么多次,就是求众数
然后就可以用莫队算法处理询问,然后解决~~
code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<string>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#define ll long long
using namespace std;
const int maxn=200000+10;
int num[maxn],cnt[maxn];
int S[maxn];
struct arr{
int x,y;
}p[maxn];
int n,m;
int N;
bool cmp(arr a,arr b)
{
return a.x<b.x;
}
struct ar{
int l,r;
int id;
}q[maxn];
int belong[maxn];
int block;
int ans[maxn];
int now;
bool cmp_b(ar a,ar b)
{
if (belong[a.l]<belong[b.l]) return 1;
else if ((belong[a.l]==belong[b.l])&&(belong[a.r]<belong[b.r])) return 1;
return 0;
}
void revise(int x,int flag)
{
if (flag==1)
{if (now==num[S[x]]) now++;}
else
if (now==num[S[x]]&&cnt[num[S[x]]]==1) now--;
cnt[num[S[x]]]--;
num[S[x]]+=flag;
cnt[num[S[x]]]++;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&S[i]);
p[i].x=S[i],p[i].y=i;
}
sort(p+1,p+n+1,cmp);
N=1; S[p[1].y]=1;
for (int i=2;i<=n;i++)
{
if (p[i].x!=p[i-1].x) N++;
S[p[i].y]=N;
}
block=sqrt(n);
for (int i=1;i<=n;i++)
{
belong[i]=i/block+1;
}
for (int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+m+1,cmp_b);
int l=0,r=0;
for (int i=1;i<=m;i++)
{
while(r<q[i].r) revise(r+1,1),r++;
while(r>q[i].r) revise(r,-1),r--;
while(l<q[i].l) revise(l,-1),l++;
while(l>q[i].l) revise(l-1,1),l--;
ans[q[i].id]=now;
}
for (int i=1;i<=m;i++)
printf("%d\n",-ans[i]);
}