容易发现相当于求2m种操作序列所得的每种线段树tag数量之和。显然考虑每个点的贡献,也即有多少种方案会使该点上有tag。可以将点分为四类:
1.修改时被经过且有儿子被修改的节点
2.修改时被经过且没有儿子被修改的节点
3.修改时未被经过且有兄弟被修改的节点
4.修改时未被经过且没有兄弟被修改的节点
考虑一次修改对这些点的影响。设该次操作之间已进行p次修改。易得:
1.方案数不变
2.方案数+2p
3.方案数+修改前该点到根路径(包括该点自身)上有tag的方案数
4.方案数*2
只要考虑维护3类点所需的东西即可。对此易得:
1.方案数不变
2.方案数+2p
3.方案数*2
4.与其最近的非4类点祖先相同
维护两棵线段树即可。其中第一棵可以转化为维护概率来避免打lazy标记,当然并不重要。
注意维护3类点取出方案数时需要pushdown,并且对此处的pushdown需要小心是否有写丑导致四倍空间不够用。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define P 998244353 #define N 100010 #define inv 499122177 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,tree[N<<2],p[N],p2[N],stk[N],ans; int val[N<<2],taga[N<<2],tagm[N<<2]; void del(int x){ans=(ans+P-x)%P;} void ins(int x){ans=(ans+x)%P;} void updatea(int k,int x) { val[k]=(val[k]+x)%P; taga[k]=(taga[k]+x)%P; } void downa(int k) { updatea(k<<1,taga[k]); updatea(k<<1|1,taga[k]); taga[k]=0; } void updatem(int k,int x) { val[k]=1ll*val[k]*x%P; tagm[k]=1ll*tagm[k]*x%P; taga[k]=1ll*taga[k]*x%P; } void downm(int k) { updatem(k<<1,tagm[k]); updatem(k<<1|1,tagm[k]); tagm[k]=1; } void add(int k,int l,int r,int x,int y,int p) { if (l==x&&r==y) {updatea(k,p);return;} if (tagm[k]!=1) downm(k); if (taga[k]!=0) downa(k); int mid=l+r>>1; if (y<=mid) { updatem(k<<1|1,2); add(k<<1,l,mid,x,y,p); } else if (x>mid) { updatem(k<<1,2); add(k<<1|1,mid+1,r,x,y,p); } else add(k<<1,l,mid,x,mid,p),add(k<<1|1,mid+1,r,mid+1,y,p); } int query(int k) { int top=0; for (int i=k;i;i>>=1) stk[++top]=i; for (int i=top;i>1;i--) { if (tagm[stk[i]]!=1) downm(stk[i]); if (taga[stk[i]]!=0) downa(stk[i]); } return val[k]; } void modify(int k,int l,int r,int x,int y,int p) { if (l==x&&r==y) { del(tree[k]); tree[k]=1ll*(tree[k]+1)*inv%P; ins(tree[k]); return; } del(tree[k]); tree[k]=1ll*tree[k]*inv%P; ins(tree[k]); int mid=l+r>>1; if (y<=mid) { del(tree[k<<1|1]); tree[k<<1|1]=1ll*(tree[k<<1|1]+1ll*query(k<<1|1)*p%P)*inv%P; ins(tree[k<<1|1]); modify(k<<1,l,mid,x,y,p); } else if (x>mid) { del(tree[k<<1]); tree[k<<1]=1ll*(tree[k<<1]+1ll*query(k<<1)*p%P)*inv%P; ins(tree[k<<1]); modify(k<<1|1,mid+1,r,x,y,p); } else modify(k<<1,l,mid,x,mid,p),modify(k<<1|1,mid+1,r,mid+1,y,p); } void fill(int k,int l,int r){tagm[k]=1;if (l==r) return;fill(k<<1,l,l+r>>1);fill(k<<1|1,(l+r>>1)+1,r);} int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); p[0]=1;for (int i=1;i<=m;i++) p[i]=(p[i-1]<<1)%P; p2[0]=1;for (int i=1;i<=m;i++) p2[i]=1ll*p2[i-1]*inv%P; int cnt=0;fill(1,1,n); for (int _=1;_<=m;_++) { int op=read(); if (op==1) { int l=read(),r=read(); modify(1,1,n,l,r,p2[cnt]); add(1,1,n,l,r,p[cnt]); cnt++; } else printf("%d\n",1ll*ans*p[cnt]%P); } return 0; }