#loj3094 [BJOI2019]删数

不妨换个想法,构造出一个可以被删光的数列,最大化这个数列和原来数列相同的元素个数

显然一个可以被删光的数列可以分成几段相同的数字,并且每段数字的值刚好等于小于等于这种数字的元素个数

那么可以想出一个naive的dp做法,去dp这些段,

转移方程为

\[dp(i)=max(dp(j)+min(i-j,cnt(i)))\]

这个方程是有组合意义的,数轴上有若干个形如\((i-cnt(i)+1,i)\)的区间

那么\(dp(i)\)代表的就是\((1,i)\)这段区间中线段的并长度

既然是线段求并显然可以转化为区间加然后数0的个数

线段树维护区间最小值及其个数即可了

而操作2相当于更改询问的区间,这个也很简单

注意一件事情,如果一个线段的开头不在询问区间当中,这个线段会被完全删除

在询问区间左移或者右移的时候记得删除这些线段就行了

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;const int N=7*1e5+10;typedef long long ll;
map <int,int> mp;
struct linetree
{
    int mi[N<<2];int micnt[N<<2];int add[N<<2];
    inline void pd(int p,int p1,int p2)
    {
        if(add[p])
            mi[p1]+=add[p],mi[p2]+=add[p],
            add[p1]+=add[p],add[p2]+=add[p],add[p]=0;
    }
    inline void mg(int p,int p1,int p2)
    {
        if(mi[p1]==mi[p2])
            mi[p]=mi[p1],micnt[p]=micnt[p1]+micnt[p2];
        else if(mi[p1]<mi[p2])
            mi[p]=mi[p1],micnt[p]=micnt[p1];
        else
            mi[p]=mi[p2],micnt[p]=micnt[p2];
    }
    inline void build(int p,int l,int r)
    {
    //  printf("build %d %d %d\n",p,l,r);
        if(r-l==1){mi[p]=mp[r],micnt[p]=1;return;}
        int mid=(l+r)/2;
        build(p<<1,l,mid);build(p<<1|1,mid,r);
        mg(p,p<<1,p<<1|1);
    }
    inline void stadd(int p,int l,int r,int dl,int dr,int pl)
    {
        if(dl==l&&dr==r)
        {
            add[p]+=pl;mi[p]+=pl;return;
        }int mid=(l+r)/2;
        pd(p,p<<1,p<<1|1);
        if(dl<mid)stadd(p<<1,l,mid,dl,min(dr,mid),pl);
        if(mid<dr)stadd(p<<1|1,mid,r,max(dl,mid),dr,pl);
        mg(p,p<<1,p<<1|1);
    }
    inline void qry(int p,int l,int r,int dl,int dr)
    {
        if(dl==l&&dr==r){mg(0,0,p);return;}
        int mid=(l+r)/2;
        pd(p,p<<1,p<<1|1);
        if(dl<mid)qry(p<<1,l,mid,dl,min(dr,mid));
        if(mid<dr)qry(p<<1|1,mid,r,max(dl,mid),dr);
    }
    inline int cqry(int l,int r,int dl,int dr)
    {
    //  printf("cqry %d %d %d %d\n",l,r,dl,dr);
        mi[0]=0x3f3f3f3f;micnt[0]=0;
        qry(1,l,r,dl,dr);return micnt[0]*(mi[0]==0);
    }
}lt;int RGL;int RGR;int we[N];int QL;int QR;
map <int,int> cnt;int n;int m;int add;
inline void inc(int val)
{
    
    int pos=val-(++cnt[val])+1;
    if(val<=QR)
    {
        //printf("inc %d\n",val);
        lt.stadd(1,RGL,RGR,pos-1,pos,1);
    }
}
inline void dec(int val)
{
    
    int pos=val-(cnt[val]--)+1;
//  printf("dec %d,pos=%d",val,pos);
    if(val<=QR)
    {
        //printf("dec %d\n",val);
        lt.stadd(1,RGL,RGR,pos-1,pos,-1);
    }
}
inline void ins(int val)
{
//  printf("ins %d %d\n",val-cnt[val],val);
    if(cnt[val])lt.stadd(1,RGL,RGR,val-cnt[val],val,1);
}
inline void del(int val)
{
//  printf("del %d %d\n",val-cnt[val],val);
    if(cnt[val])lt.stadd(1,RGL,RGR,val-cnt[val],val,-1);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&we[i]);
    for(int i=1;i<=n;i++)cnt[we[i]]++;
    for(map <int,int> :: iterator it=cnt.begin();it!=cnt.end();++it)
        for(int j=it->first,k=1;k<=it->second;j--,k++)mp[j]++;
    RGL=-n-m;RGR=n+m;
    QL=0;QR=n;
    lt.build(1,RGL,RGR);
    for(int i=1,p;i<=m;i++)
    {
        scanf("%d",&p);
        if(p>0)
            dec(we[p]),scanf("%d",&we[p]),we[p]-=add,inc(we[p]);
        else
        {
            scanf("%d",&p);add+=p;
            if(p==-1)
                QR++,QL++,ins(QR);
            else
                del(QR),QR--,QL--;
        }
    //  printf("qry %d %d\n",QL,QR);
        printf("%d\n",lt.cqry(RGL,RGR,QL,QR));
    }return 0;
}
上一篇:##DBUtils工具类的正确使用(一)


下一篇:矩阵的正交三角分解(UQ、QR分解)