P5522 [yLOI2019] 棠梨煎雪(線段樹、狀壓)
阿新 • • 發佈:2021-09-28
首先看到題目要求實現區間詢問和單點修改,又發現題目有一條特殊性質——\(n\leq30\)。由此可以想到對 \(m\) 建一棵線段樹,每個節點維護字串壓縮後的資訊。
接下來思考如何狀壓這個字串。字串中有三種字元 0
,1
,?
。可以將 0
狀壓為 \((10)_2\),1
狀壓為 \((01)_2\),?
狀壓為 \((11)_2\)。每次問一個區間內字串的方案數就相當於將所有點進行 &
運算,這樣可以保證 ?
在碰到 0
或 1
的時候可以直接變成對應數字,而當一個 0
和 1
做 &
運算的時候對應值會變為 0
,在查詢的時候方便特判無解的情況。
以上操作對修改同理。
一些小細節:線段樹走到空區間的時候傳一個 unsigned long long
1
的整數即可。
#include<bits/stdc++.h> using namespace std; #define int long long #define Int unsigned long long template<typename _T> inline void read(_T &x) { x=0;char s=getchar();int f=1; while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();} while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();} x*=f; } #define lowbit(x) (x&(-x)) #define gb(x) ((x-1)/T + 1) #define gl(x) ((x-1)*T + 1) #define pb push_back #define fi first #define sd second #define Re register #define MOD(x) (x = (x + mod)%mod) #define pii pair<int,int> const int np = 1e5 + 15; char c[np][40]; unsigned int INF; int a[np]; char s[np]; struct node{ int l,r; int state; node *ls,*rs; inline bool inrange(int L,int R){return L <= l && r <= R;} inline bool outofrange(int L,int R){return r<L||R<l;} inline void pushup() { state = ls->state & rs->state ; } inline void upd(int L,int R,int vl) { if(inrange(L,R)) { state = vl; return; } else { if(!outofrange(L,R)) { ls->upd(L,R,vl); rs->upd(L,R,vl); pushup(); } else return; } } inline int query(int L,int R) { if(inrange(L,R)) { return state; } else { if(!outofrange(L,R)) { return ls->query(L,R) & rs->query(L,R); } else return INF; } } }mem[np * 2],*pool = mem,*rot; inline node *New(){return ++pool;} inline node *build(int L,int R) { node *u = New(); u->l = L, u->r = R; if(L == R) { u->ls = u->rs = NULL; u->state = a[L]; } else { int mid = L + R >> 1; u->ls = build(L,mid); u->rs = build(mid + 1,R); u->pushup(); } return u; } inline int solve(char *g,int len) { int state = 0; for(int i=1;i<=len;i++) { if(g[i] == '1') { state += 1ll<<(2*(i-1)); } if(g[i] == '0') { state += 2ll<<(2*(i-1)); } if(g[i] == '?') { state += 3ll<<(2*(i-1)); } } return state; } signed main() { INF = 0; INF--; int n,m,q; read(n); read(m); read(q); for(int i=1;i<=m;i++) { scanf("%s",c[i] + 1); int op = strlen(c[i] + 1); a[i] = solve(c[i],op); } rot = build(1,m); int Ans = 0; for(int i=1,opt,l,r,pos;i<=q;i++) { read(opt); switch(opt) { case 0:{ read(l); read(r); int k = rot->query(l,r); int g = 3,ans = 0; for(int i=0;i<2 * n;i+=2) { if((g<<i&k) == g<<i) ans++; if(!((k>>i)&3)) { ans = -1; break; } } if(ans == -1) Ans ^= 0; else Ans ^= (1ll<<ans); break; } case 1:{ read(pos); scanf("%s",s+1); int p_ = solve(s,strlen(s + 1)); rot->upd(pos,pos,p_); break; } } } printf("%lld\n",Ans); }
End
2020 年暑假 @一扶蘇一 給我們講了線段樹,這種指標形式實現的線段樹就是當年的成果。這道題就是當時的作業題,當時機房裡只有 cyc 做了出來。當時扶蘇和 cyc 一塊給我們講了很久,但是仍舊不明白如何實現。當一年之後,我已經熟練掌握了狀壓和線段樹,再看到這個題,心裡只想著一句話「這題似乎不是特別難」,即使這樣,這題在我心裡也有著不同於其它題目的分量。
感謝扶蘇學長。撒花~