dtoi4375「BJOI2019」删数

Posted 1124828077ccj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dtoi4375「BJOI2019」删数相关的知识,希望对你有一定的参考价值。

题意:

     对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空。一次删数操作定义如下:

  • 记当前数列长度为 k,则删掉数列中所有等于 k 的数。

     现有一个长度为 n 的数列 a,有 m 次修改操作,第 i 次修改后你要回答:经过 i 次修改后的数列 a,至少还需要修改几个数才可删空?

     每次修改操作为单点修改或数列整体加一或数列整体减一。

题解:

     如果一个我要删去大小为a的数,那么序列长度会变成a-h[a](h[a]为数值a出线的次数)。那么我们意会一下这个情况,我们可以想象成有h[a]个箱子堆在了a上面,然后向左倾倒,一个一个地落在位置上。当然,大于序列长度的位置的箱子不能考虑进来。

     那么答案是什么?答案就是没有箱子的位置数量。因为我可以把堆了多个箱子的位置上的箱子移到没有箱子的地方,移动次数也就是答案。

     那么如果只有单点修改的话,我们用一个线段树维护一下就好了。

     然而有数列整体加减1怎么办呢。没有关系,我们只需要当成数字没变,询问的区间位移了就可以了。当然,后加入的数字要跟着位移。

     于是我们又可以使用线段树维护了!

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int INF=300000;
int n,m,a[300002],h[900002],t,cnt=1,minn,ans;
typedef struct{
    int ls,rs,Min,sum,f;
}P;
P p[4000002];
void pushdown(int root){
    if (p[root].f)
    {
        if (p[root].ls)
        {
            p[p[root].ls].Min+=p[root].f;p[p[root].ls].f+=p[root].f;
        }
        if (p[root].rs)
        {
            p[p[root].rs].Min+=p[root].f;p[p[root].rs].f+=p[root].f;
        }
        p[root].f=0;
    }
}
void build(int root,int begin,int end){
    if (begin==end)
    {
        p[root].Min=0;p[root].sum=1;
        return;
    }
    int mid=begin+(end-begin)/2;
    p[root].ls=++cnt;p[root].rs=++cnt;
    build(p[root].ls,begin,mid);build(p[root].rs,mid+1,end);
    int ls=p[root].ls,rs=p[root].rs;
    if (p[ls].Min<p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum;}
    else if (p[ls].Min>p[rs].Min){p[root].Min=p[rs].Min;p[root].sum=p[rs].sum;}
    else if (p[ls].Min==p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum+p[rs].sum;}
}
void gengxin(int root,int begin,int end,int begin2,int end2,int x){
    if (begin>end2 || end<begin2)return;
    if (begin>=begin2 && end<=end2)
    {
        p[root].Min+=x;p[root].f+=x;
        return;
    }
    int mid=begin+(end-begin)/2;pushdown(root);
    gengxin(p[root].ls,begin,mid,begin2,end2,x);gengxin(p[root].rs,mid+1,end,begin2,end2,x);
    int ls=p[root].ls,rs=p[root].rs;
    if (p[ls].Min<p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum;}
    else if (p[ls].Min>p[rs].Min){p[root].Min=p[rs].Min;p[root].sum=p[rs].sum;}
    else if (p[ls].Min==p[rs].Min){p[root].Min=p[ls].Min;p[root].sum=p[ls].sum+p[rs].sum;}
}
void chaxun(int root,int begin,int end,int begin2,int end2){
    if (begin>end2 || end<begin2)return;
    if (begin>=begin2 && end<=end2)
    {
        if (p[root].Min<minn)
        {
            minn=p[root].Min;ans=p[root].sum;
        }
        else if (p[root].Min==minn)ans+=p[root].sum;
        return;
    }
    int mid=begin+(end-begin)/2;pushdown(root);
    chaxun(p[root].ls,begin,mid,begin2,end2);chaxun(p[root].rs,mid+1,end,begin2,end2);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){scanf("%d",&a[i]);h[a[i]+INF]++;}
    t=n;build(1,-INF,INF);
    for (int i=1;i<=n;i++)
    if (h[i+INF])gengxin(1,-INF,INF,i-h[i+INF]+1,i,1);
    for (int i=1;i<=m;i++)
    {
        int op,x;
        scanf("%d%d",&op,&x);
        if (op)
        {
            if (h[a[op]+INF] && a[op]<=t)gengxin(1,-INF,INF,a[op]-h[a[op]+INF]+1,a[op]-h[a[op]+INF]+1,-1);
            h[a[op]+INF]--;x=t-n+x;h[x+INF]++;
            if (h[x+INF])gengxin(1,-INF,INF,x-h[x+INF]+1,x-h[x+INF]+1,1);
            a[op]=x;
        }
        else
        {
            if (x==-1)
            {
                t++;
                if (h[t+INF])gengxin(1,-INF,INF,t-h[t+INF]+1,t,1);
            }
            else
            {
                if (h[t+INF])gengxin(1,-INF,INF,t-h[t+INF]+1,t,-1);
                t--;
            }
        }
        minn=INF;ans=0;
        chaxun(1,-INF,INF,t-n+1,t);
        if (minn)puts("0");else printf("%d
",ans);
    }
    return 0;
}

以上是关于dtoi4375「BJOI2019」删数的主要内容,如果未能解决你的问题,请参考以下文章

BJOI2019 删数

[BJOI2019] 删数

Luogu5324 BJOI2019删数(线段树)

[BJOI2019]排兵布阵

「BJOI2019」奥术神杖

[BJOI2019]排兵布阵