Link:
Solution:
lxl出的语文题
其实转化一下就是求将当前区间最少拆分成多少个严格单调上升序列(可不连续)
再转化一下就是求区间内的众数个数
本来求众数的套路是主席树+二分
但此题不要求在线,用莫队同时维护$i$的出现次数$cnt[i]$和出现次数为$i$的数的个数$sum[i]$
这样在删除时仅当$sum[cnt[dat[pos]]]==1$时才减少$cur$
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=5e5+10; struct Query{int l,r,id;}qry[MAXN]; int n,m,cnt[MAXN],sum[MAXN],res[MAXN],dat[MAXN],dsp[MAXN],tot,blk,cur; int cal(int x){return (x-1)/blk+1;} bool cmp(Query x,Query y) {return cal(x.l)==cal(y.l)?x.r<y.r:x.l<y.l;} void upd(int pos,int val) { int &k=cnt[dat[pos]]; if(val==1&&cur==k) cur++; else if(val==-1&&cur==k&&sum[cur]==1) cur--; sum[k]--;sum[k+val]++;k+=val; } int main() { scanf("%d%d",&n,&m);blk=(int)sqrt(n); for(int i=1;i<=n;i++) scanf("%d",&dat[i]),dsp[i]=dat[i]; sort(dsp+1,dsp+n+1);tot=unique(dsp+1,dsp+n+1)-dsp-1; for(int i=1;i<=n;i++) dat[i]=lower_bound(dsp+1,dsp+tot+1,dat[i])-dsp; for(int i=1;i<=m;i++) scanf("%d%d",&qry[i].l,&qry[i].r),qry[i].id=i; sort(qry+1,qry+m+1,cmp); int l=1,r=0; for(int i=1;i<=m;i++) { while(l>qry[i].l) upd(--l,1); while(r<qry[i].r) upd(++r,1); while(l<qry[i].l) upd(l++,-1); while(r>qry[i].r) upd(r--,-1); res[qry[i].id]=-cur; } for(int i=1;i<=m;i++) printf("%d ",res[i]); return 0; }