【BJOI2019】刪數 線段樹

題目大意:一個數列若能在有限次數內刪空,則稱這個數列能夠刪空,一次刪除操做定義以下:c++

記當前數列長度爲$k$,則刪掉數列中全部等於$k$的數。ui

如今有一個長度爲$n$的數列$a$,有$m$次修改操做,爲單點變值/總體增長或者減小$1$,問每次修改後,最少須要修改序列中多少個數,使得序列能夠被刪除。spa

數據範圍:$n≤150000$。code

 

咱們首先考慮下最少須要修改的次數,咱們設$b[i]$爲數列$a$中填寫了i的值得數量。blog

對於每個$i$,咱們能夠用$b[i]$這麼多數,覆蓋區間$[i-b[i]+1,i]$。最終的答案就是未被覆蓋的格子數量。it

證實顯然。class

 

基於這個結論,咱們就能夠在$O(n)$的複雜度內求出一個序列$a$對應的答案,能夠得到47分的好成績。數據

在只有單點修改的狀況下,咱們發現咱們能夠用線段樹作一下維護,就能夠得到60分的好成績。查詢

咱們發現,總體的$+1$或者$-1$,能夠轉化爲查詢的區間出現了移動,移動完查詢區間後,咱們更新一下兩端的值便可。移動

這麼搞就能夠過掉這一題了。

時間複雜度:$O(n\log\ 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\n",query(1,T-move+1,T-move+n));
59     }
60 }
相關文章
相關標籤/搜索