• 【BJOI2019】删数 线段树


    题目大意:一个数列若能在有限次数内删空,则称这个数列可以删空,一次删除操作定义如下:

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

    现在有一个长度为$n$的数列$a$,有$m$次修改操作,为单点变值/整体增加或者减少$1$,问每次修改后,最少需要修改序列中多少个数,使得序列可以被删除。

    数据范围:$n≤150000$。

    我们首先考虑下最少需要修改的次数,我们设$b[i]$为数列$a$中填写了i的值得数量。

    对于每一个$i$,我们可以用$b[i]$这么多数,覆盖区间$[i-b[i]+1,i]$。最终的答案就是未被覆盖的格子数量。

    证明显然。

    基于这个结论,我们就可以在$O(n)$的复杂度内求出一个序列$a$对应的答案,可以获得47分的好成绩。

    在只有单点修改的情况下,我们发现我们可以用线段树做一下维护,就可以获得60分的好成绩。

    我们发现,整体的$+1$或者$-1$,可以转化为查询的区间出现了移动,移动完查询区间后,我们更新一下两端的值即可。

    这么搞就可以过掉这一题了。

    时间复杂度:$O(nlog n)$。

     1 #include<bits/stdc++.h>
     2 #define M (1<<19)
     3 using namespace std;
     4 
     5 struct seg{int l,r,tag,minn,cnt;}a[M<<1];
     6 void pushup(int x){
     7     a[x].minn=min(a[x<<1].minn,a[x<<1|1].minn);
     8     a[x].cnt=(a[x].minn==a[x<<1].minn?a[x<<1].cnt:0)+(a[x].minn==a[x<<1|1].minn?a[x<<1|1].cnt:0);
     9 }
    10 void upd(int x,int k){a[x].minn+=k; a[x].tag+=k;}
    11 void pushdown(int x){if(a[x].tag) upd(x<<1,a[x].tag),upd(x<<1|1,a[x].tag); a[x].tag=0;}
    12 
    13 void build(int x,int l,int r){
    14     a[x].l=l; a[x].r=r; if(l==r) return void(a[x].cnt=1);
    15     int mid=(l+r)>>1;
    16     build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    17     pushup(x);
    18 }
    19 void updata(int x,int l,int r,int k){
    20     if(l<=a[x].l&&a[x].r<=r) return upd(x,k);
    21     pushdown(x); int mid=(a[x].l+a[x].r)>>1;
    22     if(l<=mid) updata(x<<1,l,r,k);
    23     if(mid<r) updata(x<<1|1,l,r,k);
    24     pushup(x);
    25 }
    26 void updata(int x,int id,int k){return updata(x,id,id,k);}
    27 int query(int x,int l,int r){
    28     if(a[x].minn>0) return 0;
    29     if(l<=a[x].l&&a[x].r<=r) return a[x].cnt;
    30     pushdown(x); int mid=(a[x].l+a[x].r)>>1,cnt=0;
    31     if(l<=mid) cnt+=query(x<<1,l,r);
    32     if(mid<r) cnt+=query(x<<1|1,l,r);
    33     return cnt;
    34 }
    35 
    36 int num[M],n,q,m,orzorz[M*2]={0};
    37 int *cnt=orzorz+M;
    38 int main(){
    39     scanf("%d%d",&n,&q);
    40     for(int i=1;i<=n;i++) scanf("%d",num+i),cnt[num[i]]++;
    41     int T=max(n,q),m=T+n+q+2,move=0;
    42     build(1,1,m);
    43     for(int i=1;i<=n;i++) updata(1,T+i-cnt[i]+1,T+i,1);
    44     while(q--){
    45         int p,x; scanf("%d%d",&p,&x);
    46         if(p>0){
    47             x-=move;
    48             if(num[p]+move>=1&&num[p]+move<=n)
    49                 updata(1,num[p]-cnt[num[p]]+1+T,-1);
    50             cnt[num[p]]--; num[p]=x; cnt[num[p]]++;
    51             updata(1,num[p]-cnt[num[p]]+1+T,1);
    52         }else{
    53             if(x<0) move--;
    54             updata(1,-move-cnt[-move]+1+T,-move+T,x);
    55             updata(1,n-move-cnt[n-move]+1+T,n-move+T,-x);
    56             if(x>0) move++;
    57         }
    58         printf("%d
    ",query(1,T-move+1,T-move+n));
    59     }
    60 }
  • 相关阅读:
    JavaScript打开新窗口被拦截问题
    FileReader读取本地文件
    JS实现数组去重的6种方法总结
    cssReset
    鼠标拖拽定位和DOM各种尺寸详解
    关于前端本地压缩图片,兼容IOS/Android/PC且自动按需加载文件之lrz.bundle.js
    AJAX的get和post请求原生编写方法
    Grunt的配置和使用
    浏览器的工作流程
    Python基础——函数的迭代器和生成器
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10777784.html
Copyright © 2020-2023  润新知