题意
你猜
分析
显然答案就是所有操作子集的标记数之和。
假设当前每个点对答案的贡献都已知,考虑新加入一个操作,设为第
k
k
k个操作。
下面把点分为5类来讨论。
1、对于经过但没被定位到的节点,其贡献不变。
2、对于被定位到的点,其贡献加上
2
k
−
1
2^{k-1}
2k−1。
3、对于被定位到的点的子树中的点,其贡献乘2。
4、对于是第1类点的儿子且不属于上面三类点的节点,其贡献的增量为有多少个操作子集使得根到该节点的链中至少有一个节点有标记。
5、对于位于第4类点子树中的点,其贡献乘2。
发现我们只要多维护一个变量记录有多少个操作子集使得根到该点的路径中至少一个点有标记,这个变量同样可以通过讨论+打标记来维护。
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ls d<<1
#define rs d<<1|1
typedef long long LL;
const int N=100005;
const int MOD=998244353;
int n,m,bin,now,tag1[N*8],tag2[N*8],tag3[N*8],val[N*8],f[N*8],ans[N*8];
void mark1(int d,int w)
{
tag1[d]=(LL)tag1[d]*w%MOD;
val[d]=(LL)val[d]*w%MOD;
ans[d]=(LL)ans[d]*w%MOD;
}
void mark2(int d,int w)
{
tag2[d]=(LL)tag2[d]*w%MOD;
tag3[d]=(LL)tag3[d]*w%MOD;
f[d]=(LL)f[d]*w%MOD;
}
void mark3(int d,int w)
{
(tag3[d]+=w)%=MOD;
(f[d]+=w)%=MOD;
}
void pushdown(int d)
{
int w=tag1[d];tag1[d]=1;
mark1(ls,w);mark1(rs,w);
w=tag2[d];tag2[d]=1;
mark2(ls,w);mark2(rs,w);
w=tag3[d];tag3[d]=0;
mark3(ls,w);mark3(rs,w);
}
void updata(int d)
{
ans[d]=(ans[ls]+ans[rs])%MOD;
(ans[d]+=val[d])%=MOD;
}
void work(int d)
{
(val[d]+=f[d])%=MOD;
mark2(d,2);
pushdown(d);
mark1(ls,2);mark1(rs,2);
updata(d);
}
void solve(int d,int l,int r,int x,int y)
{
pushdown(d);
if (x<=l&&r<=y)
{
(val[d]+=bin)%=MOD;(f[d]+=bin)%=MOD;
mark1(ls,2);mark3(ls,bin);
mark1(rs,2);mark3(rs,bin);
updata(d);
return;
}
int mid=(l+r)/2;
if (x<=mid) solve(ls,l,mid,x,y);
else work(ls);
if (y>mid) solve(rs,mid+1,r,x,y);
else work(rs);
updata(d);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n*4;i++) tag1[i]=tag2[i]=1;
for (int i=1;i<=m;i++)
{
int op;scanf("%d",&op);
if (op==2) printf("%d\n",ans[1]);
else
{
bin=(!bin?1:bin*2%MOD);
int l,r;scanf("%d%d",&l,&r);
solve(1,1,n,l,r);
}
}
return 0;
}