[洛谷3709]大爷的字符串题(区间众数)
题目描述
给你一个字符串a,每次询问一段区间的贡献
贡献定义:
每次从这个区间中随机拿出一个字符x,然后把x从这个区间中删除,你要维护一个集合S
如果S为空,你rp减1
如果S中有一个元素不小于x,则你rp减1,清空S
之后将x插入S
由于你是大爷,平时做过的题考试都会考到,所以每次询问你搞完这段区间的字符之后最多还有多少rp?rp初始为0
询问之间不互相影响~
输入输出格式
输入格式:
第一行两个数n,m,表示字符串长度与询问次数
之后一行n个数,表示字符串
由于你是大爷,所以字符集1e9
之后m行每行两个数,表示询问的左右区间
输出格式:
m行,每行一个数表示答案
输入输出样例
说明
前4个点1s,后面的点4s
对于10%的数据,是样例
对于另外10%的数据,n,m <= 100
对于另外10%的数据,n,m <= 1000
对于另外10%的数据,n,m <= 10000
对于另外10%的数据,n,m <= 100000
对于100%的数据,n,m <= 200000
题意很难懂啊
题意:求严格上升子序列的最少个数,每个数只能在一个子序列中且每个数的顺序可以自由交换,答案为个数*-1(即1 3 3 2的答案是-2),然后给出几个询问,每次询问一个区间,求最少个数。
我们发现严格上升子序列的最少个数取决于相同数字的出现个数。即严格上升子序列的最少个数为询问的区间内的众数的出现次数
莫队
由于数字最大可以是1e9,所以我们要先离散化
我们用cnt[i]表示数i的出现次数,用num[i]表示出现i次的数字个数,sum表示出现次数最多的数的出现次数(即众数的出现次数)
对于把区间向左,向右扩展一个数,我们判断扩展那个数的出现次数是不是当前区间内众数的出现次数(cnt[i]==sum),是则更新众数的出现次数(sum++),然后更新num,cnt什么的
对于把区间最左边(右边)删除一个数,我们判断删除那个数的出现次数是不是当前区间内众数的出现次数(cnt[i]==sum),是则再判断这个出现次数是不是只有这一个数(num[i]==1),若是,则意味着区间众数的出现次数变少(sum--),然后更新num,cnt什么的
num[0]的初始值要设成数字的种类数(即出现了多少种的不同数字 1 3 3 2 num[0]=3),我们可以理解为初始[l,r]区间内,有这么多个数出现了0次
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; struct xxx{ int l,r,block,id; }q[200001]; struct xxx2{ int val,id; }a[200001]; int b[200001],cnt[200001],num[200001],ans[200001]; bool cmp(xxx a,xxx b){if(a.block!=b.block)return a.block<b.block;return a.r<b.r;} bool cmp2(xxx2 a,xxx2 b){return a.val<b.val;} int main() { int n,m;scanf("%d%d",&n,&m);int T=(int)sqrt((double)n); for(int i=1;i<=n;i++){scanf("%d",&a[i].val);a[i].id=i;} sort(a+1,a+n+1,cmp2);int last=0; for(int i=1;i<=n;i++) { if(a[i].val!=a[i-1].val)last++; b[a[i].id]=last; } for(int i=1;i<=m;i++) { scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;q[i].block=(q[i].l+1)/T; } sort(q+1,q+m+1,cmp); int l=1,r=0,sum=0;num[0]=last; for(int i=1;i<=m;i++) { while(r<q[i].r){r++;if(cnt[b[r]]==sum)sum++;num[cnt[b[r]]]--;cnt[b[r]]++;num[cnt[b[r]]]++;} while(r>q[i].r){if(cnt[b[r]]==sum&&num[cnt[b[r]]]==1)sum--;num[cnt[b[r]]]--;cnt[b[r]]--;num[cnt[b[r]]]++;r--;} while(l<q[i].l){if(cnt[b[l]]==sum&&num[cnt[b[l]]]==1)sum--;num[cnt[b[l]]]--;cnt[b[l]]--;num[cnt[b[l]]]++;l++;} while(l>q[i].l){l--;if(cnt[b[l]]==sum)sum++;num[cnt[b[l]]]--;cnt[b[l]]++;num[cnt[b[l]]]++;} ans[q[i].id]=sum; } for(int i=1;i<=m;i++)printf("%d\n",-ans[i]); return 0; }