[P3709] 大爷的字符串题

Link:

P3709 传送门

Solution:

lxl出的语文题

其实转化一下就是求将当前区间最少拆分成多少个严格单调上升序列(可不连续)

再转化一下就是求区间内的众数个数

本来求众数的套路是主席树+二分

但此题不要求在线,用莫队同时维护$i$的出现次数$cnt[i]$和出现次数为$i$的数的个数$sum[i]$

这样常规套路更新结果就好了

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\n",res[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/newera/p/9363288.html