题解 P5280 【[ZJOI2019]线段树】

Owen_codeisking

2019-04-02 16:35:56

Solution

我考场上就打了 $20pts$ 暴力,后来听人口胡会了 $O(n\log n)$ 的做法。昨晚调了一个晚上没调出来,结果后来改变一下 $dp$ 的思路就 $A$ 了。。。 不得不说,这是一道很好的题目。$orz\ \text{jls}$ ### 前置知识:线段树 其实不会线段树应该也不会去 $ZJOI$。。。 ### 1、挖掘线段树修改操作的重要性质 设询问的区间为 $[ql,qr]$ ① 若 $[l,r]\cap [ql,qr]=[l,r]$ 且它的祖先**不满足**上述性质 ,那么这个区间在这轮操作中**一定会被覆盖到**,我们定义 $[l,r]$ 为**可打标记全覆盖区间**。 ② 若 $[l,r]\cap [ql,qr]=[l,r]$ 且它的祖先**满足**上述性质,那么这个区间在这轮操作中**一定不会被覆盖到**,我们定义 $[l,r]$ 为**不可打标记全覆盖区间**。 ③ 若 $[l,r]\cap [ql,qr]\neq \emptyset$ 且**不满足**性质①②,那么这个区间的 $lazytag$ **一定会被下传**,我们定义 $[l,r]$ 为**半覆盖区间**。 ④ 若 $[l,r]\cap [ql,qr]=\emptyset$ ,那么这个区间在这轮操作中**只能接受祖先的 $lazytag$**,我们定义 $[l,r]$ 为**未覆盖区间**。 概率具有**可乘性**,我们将其转化为概率,然后乘以 $2^{now}$,$now$ 为当前修改操作的次数。 ### 2、考虑维护什么哪些值 首先,我们要维护每个区间 $lazytag$ 为 $1$ 的概率 $f$ 其实对于满足性质①②③的区间都是好 $dp$ 的,就是满足性质④的区间有些麻烦。 我们发现对于满足性质④的区间只要当前区间到根**至少有一个**区间 $lazytag$ 为 $1$ 并且在操作的时候**祖先被遍历到**即可。 考虑再维护每个区间到根上**至少有一个**区间 $lazytag$ 为 $1$ 的概率 $g$ 一次 $add$: ```cpp inline void add(int rt,int x) { ans=(ans-f[rt])%mod; f[rt]=1ll*(f[rt]+x)*inv2%mod; g[rt]=1ll*(g[rt]+x)*inv2%mod; ans=(ans+f[rt])%mod; } ``` 对于可打标记全覆盖区间/半覆盖区间,直接 $add(1/0)$ 就可以了。 对于未覆盖区间,我们考虑在每次操作的可打标记覆盖区间打一个标记。若当前区间到根有 $x$ 个可打标记覆盖区间,那么只有 $x$ 次操作**都不执行**才可能使当前区间 $lazytag$ 为 $0$ 一次 $pushdown$: ```cpp inline void pushdown(int rt) { if(tag[rt]) { g[lson]=(1ll-1ll*(1ll-g[lson])*pw[tag[rt]]%mod)%mod; g[rson]=(1ll-1ll*(1ll-g[rson])*pw[tag[rt]]%mod)%mod; tag[lson]+=tag[rt];tag[rson]+=tag[rt];tag[rt]=0; } } ``` 这个思路可能是目前我理解的最简单的思路了。 时间复杂度 $O(n\log n)$ ```cpp #include <bits/stdc++.h> #define lson (rt<<1) #define rson (rt<<1|1) using namespace std; const int maxn=100000+10; const int mod=998244353; const int inv2=(mod+1)>>1; int n,m,now,ans,pw[maxn],f[maxn<<2],g[maxn<<2],tag[maxn<<2]; inline int read() { register int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return (f==1)?x:-x; } inline void add(int rt,int x) { ans=(ans-f[rt])%mod; f[rt]=1ll*(f[rt]+x)*inv2%mod; g[rt]=1ll*(g[rt]+x)*inv2%mod; ans=(ans+f[rt])%mod; } inline void pushdown(int rt) { if(tag[rt]) { g[lson]=(1ll-1ll*(1ll-g[lson])*pw[tag[rt]]%mod)%mod; g[rson]=(1ll-1ll*(1ll-g[rson])*pw[tag[rt]]%mod)%mod; tag[lson]+=tag[rt];tag[rson]+=tag[rt];tag[rt]=0; } } void update(int L,int R,int l,int r,int rt) { if(r < L || R < l){add(rt,g[rt]);return;} if(L <= l && r <= R){add(rt,1);tag[rt]++;return;} pushdown(rt);add(rt,0); int mid=(l+r)>>1; update(L,R,l,mid,lson);update(L,R,mid+1,r,rson); } int main() { n=read(),m=read(); now=pw[0]=1; for(int i=1;i<=m;i++) pw[i]=1ll*pw[i-1]*inv2%mod; int op,l,r; while(m--) { op=read(); if(op==1) l=read(),r=read(),update(l,r,1,n,1),now=2ll*now%mod; else printf("%d\n",1ll*(ans+mod)*now%mod); } return 0; } ```