• 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;
    }
  • 相关阅读:
    mysql使用命令备份和导入导出数据
    jmeter中json提取器提取多个参数给下游接口传参
    jmeter使用正则提取器返回多个参数给下游接口入参使用
    linux下分布式部署jmeter
    使用java远程启动jmeter服务报错,报错内容:Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
    Java中看今天是星期几,礼拜几
    java读取xml文件的四种方法
    Oracle 恢复删除的表
    重启Oracle命令
    Android 资源
  • 原文地址:https://www.cnblogs.com/1124828077ccj/p/12241880.html
Copyright © 2020-2023  润新知