首先要注意到复制出的两颗棵段树一个被修改一个不被修改
所以实际上就是枚举了是否进行每个操作的(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
",(ll)sum*prod%p);
}
}
}