lgP5324 [BJOI2019]刪數加強版
阿新 • • 發佈:2021-11-04
題目給定的操作等價於:
覆蓋\([i - cnt_i + 1 , i]\)
最後答案就是 \([S-T] - [S-T]中覆蓋了的長度 = ans\)
單點修改很好做,直接修改一下並集就好了
但是區間整體 +/- 1就不是很好辦了
直接平移一下區間就好了
注意到 如果一個 區間的右端點在 當前確定的區間右邊 那麼不會對答案產生貢獻
/* 區間加減,區間並 */ #include<bits/stdc++.h> #define MAXN 1000005 typedef long long ll; using namespace std; ll n,m; ll a[MAXN],cnt[MAXN]; ll lim = 450000+5; struct node{ll mi,ans,cnt,ad;}t[MAXN * 5]; void up(int rt){ t[rt].mi = min(t[rt << 1].mi , t[rt << 1 | 1].mi); t[rt].cnt = ((t[rt << 1].mi == t[rt].mi) ? t[rt << 1].cnt : 0) + ((t[rt << 1 | 1].mi == t[rt].mi) ? t[rt << 1 | 1].cnt : 0);//沒被覆蓋的區間 t[rt].ans = t[rt << 1].ans + t[rt << 1 | 1].ans; } void build(int rt , int l , int r){ if(l == r)return (void)(t[rt].ans = t[rt].cnt = 1); int mid = (l + r) >> 1; build(rt << 1 , l , mid); build(rt << 1 | 1 , mid + 1 , r); up(rt); } void change(int rt , int c){ t[rt].mi += c; t[rt].ans = (t[rt].mi == 0 ? t[rt].cnt : 0); t[rt].ad += c; } void push_down(int rt){ if(!t[rt].ad)return; change(rt << 1 , t[rt].ad); change(rt << 1 | 1 , t[rt].ad); t[rt].ad = 0; } void update(int rt , int l , int r , int x , int y , int z){ if(r < x || l > y)return; if(x <= l && r <= y){ change(rt , z); return; } push_down(rt); int mid = (l + r) >> 1; update(rt << 1 , l , mid , x , y , z); update(rt << 1 | 1 , mid + 1 , r , x , y , z); up(rt); } int que(int rt , int l , int r , int x , int y){ if(r < x || l > y)return 0; if(x <= l && r <= y)return t[rt].ans; push_down(rt); int mid = (l + r) >> 1 , zz = 0; zz = zz + que(rt << 1 , l , mid , x , y); zz = zz + que(rt << 1 | 1 , mid + 1 , r , x , y); up(rt); return zz; } void dec(int x , int v){ int k = x - cnt[x] + 1 - (v > 0); update(1 , 1 , lim , k , k , v); cnt[x] += v; } int main(){ scanf("%d%d" , &n , &m); int ST = 1 + 150000; build(1 , 1 , lim); for(int i = 1 ; i <= n ; i++){ scanf("%lld" , &a[i]) , a[i] += ST; dec(a[i] , 1); } ll p,x; while(m--){ scanf("%lld%lld" , &p , &x); if(!p){ if(x <= 0){ ST++; int pos = ST + n; if(cnt[pos])update(1 , 1 , lim , pos - cnt[pos] + 1 , pos , 1); } else{ int pos = ST + n; if(cnt[pos])update(1 , 1 , lim , pos - cnt[pos] + 1 , pos , -1); ST--; } } else{ if(a[p] <= ST + n)dec(a[p] , -1); else cnt[a[p]]--; a[p] = x + ST; if(a[p] <= ST + n)dec(a[p] , 1); else cnt[a[p]]++; } cout<<que(1 , 1 , lim , ST + 1 , ST + n)<<endl; } }
學習了一下 區間求並的寫法,之前自己的寫法太拉胯了
這道題還有加強版,發現 原來題目的操作為 每次修改操作為單點修改或數列整體加一或數列整體減一。
加強版後的題目操作為 現在有一個長度為n的數列a和m次修改操作,每次修改形如“單點修改”或“數列數值整體平移”。每次操作後,你需要求出至少還要在數列中修改多少個數才能讓數列變成合法的。
就是 上面那個整體+1,變成了區間 + k
需要用到一個 別的題的技巧 lgP4198 樓房重建 -》》》 加強up操作
考慮在 對於值域 維護一個 每一個位置往左的最遠距離
之後就按程式碼裡面的 up 時候\(O(logn)\)維護一下就好了
#include<bits/stdc++.h> #define MAXN 5000005 #define INF 0x3f3f3f3f using namespace std; int ST = 150000 , lim = 450000 + 5; int n,m,a[MAXN],cnt[MAXN],judge = 0; struct node{int minl,sum,sum2;}t[MAXN * 5]; inline int gao(int rt , int l , int r , int v){ if(l == r)return (min(v , t[rt].minl) <= l) ? 0 : 1; int mid = (l + r) >> 1; if(v <= mid)return gao(rt << 1 , l , mid , min(v , t[rt << 1 | 1].minl)); else return t[rt].sum2 + gao(rt << 1 | 1 , mid + 1 , r , v); } inline void build(int rt , int l , int r){ if(l == r)return (void)(t[rt].minl = INF , t[rt].sum = 1); int mid = (l + r) >> 1; build(rt << 1 , l , mid); build(rt << 1 | 1 , mid + 1 , r); t[rt].sum2 = gao(rt << 1 , l , mid , t[rt << 1 | 1].minl); t[rt].sum = t[rt << 1 | 1].sum + t[rt].sum2; t[rt].minl = min(t[rt << 1].minl , t[rt << 1 | 1].minl); } inline void update(int rt , int l , int r , int x , int y){ if(l == r)return (void)(t[rt].minl = y , t[rt].sum = (y > l) ? 1 : 0); int mid = (l + r) >> 1; if(x <= mid)update(rt << 1 , l , mid , x , y); else update(rt << 1 | 1 , mid + 1 , r , x , y); t[rt].sum2 = gao(rt << 1 , l , mid , t[rt << 1 | 1].minl); t[rt].sum = t[rt << 1 | 1].sum + t[rt].sum2; t[rt].minl = min(t[rt << 1].minl , t[rt << 1 | 1].minl); } inline void dec(int x , int v){ cnt[x + 150000] += v; update(1 , 1 , lim , x + 150000 , x + 150000 - cnt[x + 150000] + 1); } int minl = INF , zz_sum = 0; namespace io { const int SIZE = 1 << 22 | 1; char iBuf[SIZE], *iS, *iT, c; char oBuf[SIZE], *oS = oBuf, *oT = oBuf + SIZE; #define gc() (iS == iT ? iT = iBuf + fread(iS = iBuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS++) : *iS++) template<class I> void read(I &x) { int f = 1; for(c = gc(); c < '0' || c > '9'; c = gc()) if(c == '-') f = -1; for(x = 0; c >= '0' && c <= '9'; c = gc()) x = (x << 3) + (x << 1) + (c & 15); x *= f; } inline void flush () { fwrite(oBuf, 1, oS - oBuf, stdout); oS = oBuf; } inline void putc(char x) { *oS++ = x; if(oS == oT) flush(); } template<class I> void print(I x) { if(x < 0) putc('-'), x = -x; static char qu[55]; char *tmp = qu; do *tmp++ = (x % 10) ^ '0'; while(x /= 10); while(tmp-- != qu) putc(*tmp); putc('\n'); } struct flusher{ ~flusher() { flush(); } }_; } inline void que(int rt , int l , int r , int x , int y){ if(r < x || l > y)return; if(x <= l && r <= y){ if(minl == INF){ zz_sum += t[rt].sum; minl = t[rt].minl; return; } zz_sum += gao(rt , l , r , minl); minl = min(minl , t[rt].minl); return; } int mid = (l + r) >> 1; que(rt << 1 | 1 , mid + 1 , r , x , y); que(rt << 1 , l , mid , x , y); } int main(){ io :: read(n) , io :: read(m); for(int i = 1 ; i <= n ; i++){ io :: read(a[i]) , cnt[a[i] + 150000]++; } build(1 , 1 , lim); for(int i = 1 ; i <= n ; i++){ if(!cnt[i + 150000])continue; update(1 , 1 , lim , i + 150000 , i + 150000 - cnt[i + 150000] + 1); } int p,x,pp = 0; while(m--){ io :: read(p); io :: read(x); if(!p)pp += x; else{ dec(a[p] , -1); a[p] = x - pp; dec(a[p] , 1); } minl = INF , zz_sum = 0; que(1 , 1 , lim , ST + 1 - pp , ST + n - pp); io :: print(zz_sum); } } /* 3 9 1 2 3 0 1 每個節點都有一個往左擴充套件的區間長度 */