一、题目
二、解法
考虑修改操作对每个点的影响,我们可以按照与修改区间的关系把点分类(相交,包含,相离),在此基础上,包含或相离节点的状态还与其父亲有关系,所以它们还要按照父亲的状态再次分类。
具体可以分为一下五类:
- 与修改区间相交,但不包含在修改区间内部的节点。
- 包含在修改区间内部,但其父亲不存在或者不包含在修改区间内部。
- 与修改区间相离,但是其父亲与修改区间相交。
- 包含在修改区间内部,其父亲也包含在修改区间内部。
- 与修改区间相离,其父亲也与修改区间相离。
分析影响:
- 对于第一类节点,操作后无标记。
- 对于第二类节点,操作后有标记。
- 对于第三类节点,有无标记取决于这个节点到根的链上是否有标记。
- 对于第四类和第五类节点,操作无影响。
设 \(f[u]\) 表示 \(u\) 有标记的占比,\(g[u]\) 表示 \(u\) 的链上有标记的占比:
- 对于第一类节点,一半保持原样,另一半无标记,\((f[u],g[u])=(\frac{1}{2}f[u],\frac{1}{2}g[u])\)
- 对于第二类节点,一半保持原样,另一半有标记,\((f[u],g[u])=(\frac{1}{2}f[u]+\frac{1}{2},\frac{1}{2}g[u]+\frac{1}{2})\)
- 对于第三类节点,一半保持原样,另一半取决于 \(u\) 到根的路径,\((f[u],g[u])=(\frac{1}{2}f[u]+\frac{1}{2}g[u],g[u])\)
- 对于第四类节点,一半保持原样,另一半点 \(u\) 不受影响,但是到根路径一定有标记:\((f[u],g[u])=(f[u],\frac{1}{2}g[u]+\frac{1}{2})\)
- 对于第五类节点,一半保持原样,另一半也不受影响:\((f[u],g[u])=(f[u],g[u])\)
前三类的节点数都是 \(O(\log n)\) 的,后两类的节点数是 \(O(n)\) 的,所以必须以打标记的方式维护第四类节点。
时间复杂度 \(O(n\log n)\)
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 400005;
#define int long long
const int MOD = 998244353;
const int inv = (MOD+1)/2;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,p,fl[M],f[M],g[M],s[M];
void work(int i,int c)
{
g[i]=(g[i]*c+1-c+MOD)%MOD;
fl[i]=fl[i]*c%MOD;
}
void upd(int i,int ty)
{
s[i]=f[i];
if(!ty) s[i]=(s[i]+s[i<<1]+s[i<<1|1])%MOD;
}
void down(int i)
{
if(fl[i]==1) return ;
work(i<<1,fl[i]);work(i<<1|1,fl[i]);
fl[i]=1;
}
void dfs(int i,int l,int r,int L,int R)
{
if(L>r || l>R)//3
{
f[i]=(f[i]+g[i])*inv%MOD;
upd(i,l==r);return ;
}
if(L<=l && r<=R)//2
{
f[i]=(f[i]+1)*inv%MOD;
upd(i,l==r);
work(i,inv);//2&4
return ;
}
int mid=(l+r)>>1;down(i);
f[i]=f[i]*inv%MOD;//1
g[i]=g[i]*inv%MOD;
dfs(i<<1,l,mid,L,R);
dfs(i<<1|1,mid+1,r,L,R);
upd(i,0);
}
signed main()
{
n=read();m=read();p=1;
for(int i=1;i<=4*n;i++) fl[i]=1;
while(m--)
{
int op=read();
if(op==1)
{
int l=read(),r=read();
dfs(1,1,n,l,r);
p=(p+p)%MOD;
}
else printf("%lld\n",p*s[1]%MOD);
}
}