https://www.luogu.org/blog/Sooke/solution-p5280
首先想到对线段树上每个点分别维护有多少棵线段树在它上有标记(f[]),然后想到对于每个操作,根据转移的不同分成5种点。
为了满足第三类点的转移要求,再维护g[],转移类似分类讨论即可。
最后发现前三类点是$O(log n)$级别的,后两类点可以通过打标记实现。于是就做完了。
1 #include<cstdio> 2 #include<algorithm> 3 #define ls (x<<1) 4 #define rs (ls|1) 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 using namespace std; 7 8 const int N=2000010,mod=998244353; 9 int n,m,k=1,op,l,r,f[N],g[N],tf[N],tg[N],sf[N]; 10 11 int add(int x,int y){ return x+y>=mod ? x+y-mod : x+y; } 12 int sub(int x,int y){ return x-y<0 ? x-y+mod : x-y; } 13 14 void upd(int x){ sf[x]=add(f[x],add(sf[ls],sf[rs])); } 15 void pushf(int x,int k){ f[x]=1ll*f[x]*k%mod; tf[x]=1ll*tf[x]*k%mod; sf[x]=1ll*sf[x]*k%mod; } 16 void pushg(int x,int k){ g[x]=1ll*g[x]*k%mod; tg[x]=1ll*tg[x]*k%mod; } 17 18 void push(int x){ 19 if (tf[x]!=1) pushf(ls,tf[x]),pushf(rs,tf[x]),tf[x]=1; 20 if (tg[x]!=1) pushg(ls,tg[x]),pushg(rs,tg[x]),tg[x]=1; 21 } 22 23 void build(int x,int L,int R){ 24 g[x]=tf[x]=tg[x]=1; 25 if (L==R) return; 26 int mid=(L+R)>>1; 27 build(ls,L,mid); build(rs,mid+1,R); 28 } 29 30 void mdf(int x,int L,int R,int l,int r){ 31 push(x); 32 if (L==l && r==R){ f[x]=add(f[x],k); pushf(ls,2); pushf(rs,2); upd(x); return; } 33 int mid=(L+R)>>1; g[x]=add(g[x],k); 34 if (r<=mid){ 35 mdf(ls,L,mid,l,r); push(rs); f[rs]=add(f[rs],sub(k,g[rs])); g[rs]=add(g[rs],g[rs]); 36 pushf(rs<<1,2); pushf((rs<<1)|1,2); pushg(rs<<1,2); pushg((rs<<1)|1,2); upd(rs); 37 }else if (l>mid){ 38 mdf(rs,mid+1,R,l,r); push(ls); f[ls]=add(f[ls],sub(k,g[ls])); g[ls]=add(g[ls],g[ls]); 39 pushf(ls<<1,2); pushf((ls<<1)|1,2); pushg(ls<<1,2); pushg((ls<<1)|1,2); upd(ls); 40 }else mdf(ls,L,mid,l,mid),mdf(rs,mid+1,R,mid+1,r); 41 upd(x); 42 } 43 44 int main(){ 45 freopen("segment.in","r",stdin); 46 freopen("segment.out","w",stdout); 47 scanf("%d%d",&n,&m); build(1,1,n); 48 rep(i,1,m){ 49 scanf("%d",&op); 50 if (op==1) scanf("%d%d",&l,&r),mdf(1,1,n,l,r),k=add(k,k); 51 else printf("%d ",sf[1]); 52 } 53 return 0; 54 }