luogu P5324 [BJOI2019]删数

传送门

不如先考虑暴力,能删的序列首先有\(1,2,3...n\),还有就是升序排序后从后往前放数,第\(i\)位要么放\(i\),要么放\(i+1\)位置的数,例如\(1,2,4,4,5,6,9,9,9\)

如果一个数\(i\)出现了若干次,假如是\(num_i\)次,我们发现是可以在\(i,i-1,i-2...i-num_i+1\)上放\(i\)的,这样放完之后,如果有的位置没有用到现有的数放上去,那么就要从没用的数里改一个放过来,问题也就是用一堆数,最多能放多少个位置.考虑从后往前放,然后如果一个位置有多个数就接着用这种数往后放.如果放到一个位置\(j\),\(j\)有若干个,当前的\(i\)也有若干个,我们只能用一种放,那么显然要用更多的那种放.最后没放数的位置数就是答案.

为了方便,我们把放数看成区间覆盖,即一个\(i\)可以覆盖\([i-num_i+1,i]\)这段区间,我们再给所有覆盖区间加上\(1\),那就是求\([1,n]\)\(0\)的个数.注意到修改的\(+1/-1\)都是整体的,所以如果把所有数放在一根数轴上,那么初始要统计的区间为\([1,n]\),然后整体\(+1\)相当于统计区间整体向左移\(1\),然后 以原统计区间右端点 为右端点 的对应区间覆盖的贡献要减掉,因为那个右端点的数超过统计范围,不能放进来;整体\(-1\)相当于统计区间整体向右移\(1\),然后 以原统计区间右端点 为右端点 的对应区间覆盖的贡献要加上;单点修改,也就是原来的\(num_{a_p}\)\(1\),后面的新的\(num\)\(1\),这导致对应两个覆盖区间左端点右移和左移.这些东西可以用线段树维护,注意线段树的叶子数量应该是\(n+q+q\)

至于区间\(0\)的数量,用值域线段树维护因为区间\(+1\),某个位置最少为\(0\),只要维护区间最小值及数量就行了

// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double

using namespace std;
const int N=150000+10,M=N*3;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
struct node
{
    int mi,nm;
    node(){mi=0,nm=1;}
    node(int nmi,int nnm){mi=nmi,nm=nnm;}
    node operator + (const node &bb) const
    {
        if(mi==bb.mi) return node(mi,nm+bb.nm);
        return mi<bb.mi?(*this):bb;
   	}
    void ad(int x){mi+=x;}
}s[M<<2],nw;
int tg[M<<2];
void psup(int o){s[o]=s[o<<1]+s[o<<1|1];}
void psdn(int o){if(tg[o]) s[o<<1].ad(tg[o]),tg[o<<1]+=tg[o],s[o<<1|1].ad(tg[o]),tg[o<<1|1]+=tg[o],tg[o]=0;}
#define mid ((l+r)>>1)
int nl,nr;
void modif(int o,int l,int r,int ll,int rr,int x)
{
    if(ll<=l&&r<=rr){s[o].ad(x),tg[o]+=x;return;}
    psdn(o);
    if(ll<=mid) modif(o<<1,l,mid,ll,rr,x);
    if(rr>mid) modif(o<<1|1,mid+1,r,ll,rr,x);
    psup(o);
}
void modif(int lx,int x)
{
    int o=1,l=nl,r=nr,st[21],tp=0;
    while(l<r)
    {
        st[++tp]=o,psdn(o);
        if(lx<=mid) o=o<<1,r=mid;
        else o=o<<1|1,l=mid+1;
    }
    s[o].ad(x);
    while(tp)
    {
        o=st[tp--];
        psup(o);
    }
}
node quer(int o,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr) return s[o];
    psdn(o);
    node an;
    an.mi=M;
    if(ll<=mid) an=an+quer(o<<1,l,mid,ll,rr);
    if(rr>mid) an=an+quer(o<<1|1,mid+1,r,ll,rr);
    psup(o);
    return an;
}
void bui(int o,int l,int r)
{
    if(l==r) return;
    bui(o<<1,l,mid),bui(o<<1|1,mid+1,r);
    psup(o);
}
int n,q,a[N],nm[M],ll=150001,rr;


int main()
{
    n=rd(),q=rd();
    rr=ll+n-1;
    nl=ll-q,nr=rr+q;
    bui(1,nl,nr);
    for(int i=1;i<=n;++i)
    {
        a[i]=rd()+ll-1;
        ++nm[a[i]];
        modif(a[i]-nm[a[i]]+1,1);
    }
    while(q--)
    {
        int p=rd();
        if(!p)
        {
            if(~rd())
            {
                if(nm[rr]) modif(1,nl,nr,rr-nm[rr]+1,rr,-1);
                --ll,--rr;
            }
            else
            {
                ++ll,++rr;
                if(nm[rr]) modif(1,nl,nr,rr-nm[rr]+1,rr,1);
            }
        }
        else
        {
            if(a[p]<=rr) modif(a[p]-nm[a[p]]+1,-1);
            --nm[a[p]];
            a[p]=rd()+ll-1;
            ++nm[a[p]];
            modif(a[p]-nm[a[p]]+1,1);
        }
        nw=quer(1,nl,nr,ll,rr);
        printf("%d\n",nw.mi?0:nw.nm);
    }
    return 0;
}
posted @ 2019-04-25 20:17  ✡smy✡  阅读(193)  评论(0编辑  收藏  举报