首先要注意到復制出的兩顆棵段樹一個被修改一個不被修改
所以實際上就是枚舉了是否進行每個操作的\(2^m\)種情況
肯定不可能枚舉出來
我們考慮對每個節點分別統計答案
設\(f_x\)表示當前節點\(x\)的所有情況下的\(Tag\)和
考慮每次加入一個操作對其\(Tag\)的影響
設當前為第\(t\)個操作
即修改的\(2^{t-1}\)棵線段樹中
有多少棵的\(x\)節點的\(Tag\)是\(1\)
一共只有\(4\)種情況
- \(Tag\)直接被賦值為\(1\)(在這個點打標記)
- \(Tag\)直接被賦值為\(0\)(對該點進行了
pushdown
操作) - \(Tag\)沒有被修改(根本沒有訪問這個節點)
- \(Tag\)繼承自父親節點(對該點的父節點進行了
pushdown
操作但沒有修改這個節點)
考慮\(f_x\)的改變量
對於前\(3\)種情況
\(f_x\)的改變是明顯的
第一種\(f_x+=2^{t-1}\)
第二種\(f_x+=0\)
第三種\(f_x*=2\)
第四種比較復雜
\(f_x+=使這個點到根有至少一個節點的Tag為1的方案數\)
注意自己有標記也是可以的
因為只有\(1\)的標記會下傳
設這個量為\(g_x\)
考慮\(g_x\)的維護
如果在線段樹上對該點進行了pushdown
操作
那么新增的\(2^{t-1}\)棵樹的這個節點根的路徑上都不會有\(Tag=1\)
\(g_x+=0\)
如果在這個點打了標記
這個點和它的子樹到根的路徑上都一定有\(Tag=1\)
這些節點的\(g+=2^{t-1}\)
剩下的節點到根的路徑上的\(Tag\)情況不會改變
\(g_x*=2\)
考慮用線段樹模擬維護\(f\)和\(g\)
因為涉及未訪問節點
可以不修改這些節點
把訪問的節點的值除\(2\)
算答案的時候乘上\(2^t\)
但這樣\(g\)的修改不是很好維護
考慮每次修改的同時都會將其除\(2\)
所以用一個\(tag\)記錄被修改了幾次
pushdown
時將\(g_{son}\)除去\(2^{tag_x}\)
然后加上\(\frac{1}{2},\frac{1}{4},...,\frac{1}{2^{tag_x}}=1-2^{tag_x}\)
#include<bits/stdc++.h>
using namespace std;
#define gc c=getchar()
#define r(x) read(x)
#define ll long long
template<typename T>
inline void read(T&x){
T k=1;char gc;x=0;
while(!isdigit(c)){if(c=='-')k=-1;gc;}
while(isdigit(c))x=x*10+c-'0',gc;x*=k;
}
const int N=1e5+7;
const int p=998244353;
const ll Inv=(p+1)>>1;
int Pow[N];
int sum=0,prod=1;
inline int add(int a,int b){
return (a+=b)>=p?a-p:a;
}
inline int sub(int a,int b){
return (a-=b)<0?a+p:a;
}
int f[N<<2],g[N<<2],tag[N<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
inline void pushdown(int rt){
if(tag[rt]){
g[ls]=add((ll)g[ls]*Pow[tag[rt]]%p,sub(1,Pow[tag[rt]]));
g[rs]=add((ll)g[rs]*Pow[tag[rt]]%p,sub(1,Pow[tag[rt]]));
tag[ls]+=tag[rt];
tag[rs]+=tag[rt];
tag[rt]=0;
}
}
void modify(int rt,int l,int r,int x,int y){
sum=sub(sum,f[rt]);
f[rt]=f[rt]*Inv%p;
g[rt]=g[rt]*Inv%p;
if(x<=l&&r<=y){
f[rt]=add(f[rt],Inv);
g[rt]=add(g[rt],Inv);
}
else if(l>y||r<x){
f[rt]=add(f[rt],g[rt]);
g[rt]=add(g[rt],g[rt]);
}
sum=add(sum,f[rt]);
if(l>y||r<x)return ;
if(x<=l&&r<=y){
++tag[rt];
return ;
}
pushdown(rt);
int mid=(l+r)>>1;
modify(ls,l,mid,x,y);
modify(rs,mid+1,r,x,y);
}
int main(){
freopen("segment.in","r",stdin);
freopen("segment.out","w",stdout);
int n,m;r(n),r(m);
Pow[0]=1;
for(int i=1;i<=m;++i){
Pow[i]=Pow[i-1]*Inv%p;
}
while(m--){
int opt,l,r;r(opt);
if(opt==1){
r(l),r(r);
modify(1,1,n,l,r);
prod=add(prod,prod);
}
else{
printf("%lld\n",(ll)sum*prod%p);
}
}
}