[ZJOI2019]線段樹(線段樹)
阿新 • • 發佈:2019-04-02
clu build lld span ref ble def pre code
[ZJOI2019]線段樹(線段樹)
題面
洛谷
題解
首先問題等價於前面每次操作都可能進行修改或者不修改,求所有情況下有標記點的個數。
考慮依次修改操作會產生的影響,把線段樹節點進行分類。
- 這個點和以及其父親都和修改區間無交:顯然這個點的標記不會被修改。
- 這個點和修改區間無交但父親和修改區間有交:那麽這個區間有沒有標記只和本身有沒有標記以及是否存在一個祖先有標記相關。
- 這個點被修改區間完全包含,且父親沒有被完全包含:顯然這個點就是放置標記的一個點,那麽一定會有標記。
- 這個點被修改區間完全包含,且父親節點也被完全包含:那麽這個點的標記不會變化。
- 這個點和修改區間有交但沒有被完全包含:那麽這個點一定會\(pushdown\)
其中四類點的標記狀態都和其他點無關,只有第二類標記和其到根節點的所有節點相關。
那麽顯然記錄兩個狀態就好了,即這個點被標記的概率以及這個點到根節點至少有一個點被標記的概率,不妨記為\(f,g\)。
考慮如何轉移:
- 第一類點:顯然沒有變化。
- 第二類點:首先\(0.5\)的概率不會變化,然後\(0.5\)的概率變成\(g\),那麽轉移就是\(f=0.5f'+0.5*g'\);然後考慮\(g\)的轉移,還是\(0.5\)的概率不變化,\(0.5\)的概率變成\(g'\),所以轉移是\(g=0.5g'+0.5g'=g'\)。
- 第三類點:\(f=0.5f'+0.5\)
- 第四類點:\(f\)不會變化,\(g\)會變成\(0.5g'+0.5\)。
- 第五類點:\(f=0.5f',g=0.5g'\)。
第四類點似乎需要資磁區間乘法和區間加法,不過這個東西還是個線段樹模板。
#include<iostream> #include<cstdio> using namespace std; #define MAX 100100 #define MOD 998244353 #define inv2 499122177 #define lson (now<<1) #define rson (now<<1|1) inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m,pw=1,f[MAX<<3],g[MAX<<3],mul[MAX<<3],pls[MAX<<3],s[MAX<<3]; void Build(int now,int l,int r) { mul[now]=1;if(l==r)return; int mid=(l+r)>>1; Build(lson,l,mid);Build(rson,mid+1,r); } void pushup(int now){s[now]=((s[lson]+s[rson])%MOD+f[now])%MOD;} void upd(int now){f[now]=1ll*inv2*(f[now]+g[now])%MOD;pushup(now);} void puttag(int now,int m,int p) { g[now]=(1ll*g[now]*m+p)%MOD; mul[now]=1ll*mul[now]*m%MOD; pls[now]=(1ll*pls[now]*m+p)%MOD; } void pushdown(int now) { if(mul[now]==1&&pls[now]==0)return; puttag(lson,mul[now],pls[now]); puttag(rson,mul[now],pls[now]); mul[now]=1;pls[now]=0; } void Modify(int now,int l,int r,int L,int R) { if(L==l&&r==R) { f[now]=1ll*inv2*(f[now]+1)%MOD; puttag(now,inv2,inv2);pushup(now);return; } int mid=(l+r)>>1;pushdown(now); f[now]=1ll*inv2*f[now]%MOD; g[now]=1ll*inv2*g[now]%MOD; if(R<=mid)Modify(lson,l,mid,L,R),upd(rson); else if(L>mid)Modify(rson,mid+1,r,L,R),upd(lson); else Modify(lson,l,mid,L,mid),Modify(rson,mid+1,r,mid+1,R); pushup(now); } int main() { n=read();m=read(); while(m--) { int opt=read(),l,r; if(opt==1)l=read(),r=read(),Modify(1,1,n,l,r),pw=(pw+pw)%MOD; else printf("%lld\n",1ll*pw*s[1]%MOD); } return 0; }
[ZJOI2019]線段樹(線段樹)