题目描述
九条可怜是一个喜欢数据结构的女孩子,在常见的数据结构中,可怜最喜欢的就是线段树。
线段树的核心是懒标记,下面是一个带懒标记的线段树的伪代码,其中 t a g tag tag 数组为懒标记:
其中函数 Lson ( N o d e ) \operatorname{Lson}(Node) Lson(Node) 表示 N o d e Node Node 的左儿子, Rson ( N o d e ) \operatorname{Rson}(Node) Rson(Node) 表示 N o d e Node Node 的右儿子。
现在可怜手上有一棵 [ 1 , n ] [1,n] [1,n] 上的线段树,编号为 1 1 1。这棵线段树上的所有节点的 t a g tag tag 均为 0 0 0。接下来可怜进行了 m m m 次操作,操作有两种:
1 l r 1\ l\ r 1 l r,假设可怜当前手上有 t t t 棵线段树,可怜会把每棵线段树复制两份( t a g tag tag 数组也一起复制),原先编号为 i i i 的线段树复制得到的两棵编号为 2 i − 1 2i-1 2i−1 与 2 i 2i 2i,在复制结束后,可怜手上一共有 2 t 2t 2t 棵线段树。接着,可怜会对所有编号为奇数的线段树进行一次 Modify ( r o o t , 1 , n , l , r ) \operatorname{Modify}(root,1,n,l,r) Modify(root,1,n,l,r)。
2 2 2,可怜定义一棵线段树的权值为它上面有多少个节点 t a g tag tag 为 1 1 1。可怜想要知道她手上所有线段树的权值和是多少。
输入输出格式
输入格式:
第一行输入两个整数
n
,
m
n,m
n,m 表示初始区间长度和操作个数。
接下来 m m m 行每行描述一个操作,输入保证 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n。
输出格式:
对于每次询问,输出一行一个整数表示答案,答案可能很大,对
998244353
998244353
998244353 取模后输出即可。
输入输出样例
输入样例#1:
5 5
2
1 1 3
2
1 3 5
2
输出样例#1:
0
1
6
说明
[
1
,
5
]
[1,5]
[1,5] 上的线段树如下图所示:
在第一次询问时,可怜手上有一棵线段树,它所有点上都没有标记,因此答案为 0 0 0。
在第二次询问时,可怜手上有两棵线段树,按照编号,它们的标记情况为:
点
[
1
,
3
]
[1,3]
[1,3] 上有标记,权值为
1
1
1。
没有点有标记,权值为
0
0
0。
因此答案为
1
1
1。
在第三次询问时,可怜手上有四棵线段树,按照编号,它们的标记情况为:
点
[
1
,
2
]
,
[
3
,
3
]
,
[
4
,
5
]
[1,2],[3,3],[4,5]
[1,2],[3,3],[4,5] 上有标记,权值为
3
3
3。
点
[
1
,
3
]
[1,3]
[1,3] 上有标记,权值为
1
1
1。
点
[
3
,
3
]
,
[
4
,
5
]
[3,3],[4,5]
[3,3],[4,5] 上有标记,权值为
2
2
2。
没有点有标记,权值为
0
0
0。
因此答案为
6
6
6。
分析:
我们把线段树上的点分成5类。
假设操作区间为
[
x
,
y
]
[x,y]
[x,y],第一类点区间
[
l
,
r
]
[l,r]
[l,r]包含
[
x
,
y
]
[x,y]
[x,y]。也就是线段树操作经过的点,即白色点。
第二类点是修改点,即打上
t
a
g
tag
tag的点,深灰色点。
第三类点是不直接遍历到,但是可以通过下传改变的点,即橙色点。
第四类点是第二类点的儿子,第五类点是第三类点的儿子。
我们设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i次操作后,
j
j
j号节点
t
a
g
tag
tag的个数。
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示第
i
i
i次操作后,
j
j
j到根路径上不含
t
a
g
tag
tag的个数。
对于第一类点,显然所有点
t
a
g
tag
tag都被清掉,
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j]
g
[
i
]
[
j
]
=
g
[
i
−
1
]
[
j
]
+
2
i
−
1
g[i][j]=g[i-1][j]+2^{i-1}
g[i][j]=g[i−1][j]+2i−1
第二类点都打上了
t
a
g
tag
tag,
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
2
i
−
1
f[i][j]=f[i-1][j]+2^{i-1}
f[i][j]=f[i−1][j]+2i−1
g
[
i
]
[
j
]
=
g
[
i
−
1
]
[
j
]
g[i][j]=g[i-1][j]
g[i][j]=g[i−1][j]
第三类点取决于上面有没有
t
a
g
tag
tag,
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
2
i
−
1
−
g
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]+2^{i-1}-g[i-1][j]
f[i][j]=f[i−1][j]+2i−1−g[i−1][j]
g
[
i
]
[
j
]
=
2
∗
g
[
i
−
1
]
[
j
]
g[i][j]=2*g[i-1][j]
g[i][j]=2∗g[i−1][j]
第四类点和第五类点类似,都是影响不到的,只是修改后四类点的
g
g
g一定是0
f
[
i
]
[
j
]
=
2
∗
f
[
i
−
1
]
[
j
]
f[i][j]=2*f[i-1][j]
f[i][j]=2∗f[i−1][j]
g
[
i
]
[
j
]
=
0
+
g
[
i
−
1
]
[
j
]
g[i][j]=0+g[i-1][j]
g[i][j]=0+g[i−1][j]
f
[
i
]
[
j
]
=
2
∗
f
[
i
−
1
]
[
j
]
f[i][j]=2*f[i-1][j]
f[i][j]=2∗f[i−1][j]
g
[
i
]
[
j
]
=
2
∗
g
[
i
−
1
]
[
j
]
g[i][j]=2*g[i-1][j]
g[i][j]=2∗g[i−1][j]
其中一二三类点为 O ( l o g n ) O(logn) O(logn)个,四五类点为 O ( n ) O(n) O(n)个。对于四五类点,可以通过线段树打tag的方式,也就是个最浅的儿子打上乘2的tag。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=2e6+7;
const LL mod=998244353;
using namespace std;
int n,m,k,op,x,y;
LL f[maxn],g[maxn],a[maxn],b[maxn],sum[maxn];
void build(int p,int l,int r)
{
g[p]=a[p]=b[p]=1;
if (l==r) return;
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void pushf(int p,LL k)
{
f[p]=f[p]*k%mod;
sum[p]=sum[p]*k%mod;
a[p]=a[p]*k%mod;
}
void pushg(int p,LL k)
{
g[p]=g[p]*k%mod;
b[p]=b[p]*k%mod;
}
void pushdown(int p)
{
if (a[p]!=1)
{
pushf(p*2,a[p]);
pushf(p*2+1,a[p]);
a[p]=1;
}
if (b[p]!=1)
{
pushg(p*2,b[p]);
pushg(p*2+1,b[p]);
b[p]=1;
}
}
void modify(int p,int l,int r,int x,int y)
{
pushdown(p);
if ((l==x) && (r==y))
{
f[p]=(f[p]+(LL)k)%mod;
pushf(p*2,2);
pushf(p*2+1,2);
}
else
{
int mid=(l+r)/2;
g[p]=(g[p]+(LL)k)%mod;
if (y<=mid)
{
modify(p*2,l,mid,x,y);
pushdown(p*2+1);
f[p*2+1]=(f[p*2+1]+(LL)k+mod-g[p*2+1])%mod;
g[p*2+1]=g[p*2+1]*2%mod;
pushf((p*2+1)*2,2),pushf((p*2+1)*2+1,2);
pushg((p*2+1)*2,2),pushg((p*2+1)*2+1,2);
sum[p*2+1]=(sum[(p*2+1)*2]+sum[(p*2+1)*2+1]+f[p*2+1])%mod;
}
else
{
if (x>mid)
{
modify(p*2+1,mid+1,r,x,y);
pushdown(p*2);
f[p*2]=(f[p*2]+(LL)k+mod-g[p*2])%mod;
g[p*2]=g[p*2]*2%mod;
pushf((p*2)*2,2),pushf((p*2)*2+1,2);
pushg((p*2)*2,2),pushg((p*2)*2+1,2);
sum[p*2]=(sum[(p*2)*2]+sum[(p*2)*2+1]+f[p*2])%mod;
}
else
{
modify(p*2,l,mid,x,mid);
modify(p*2+1,mid+1,r,mid+1,y);
}
}
}
sum[p]=(sum[p*2]+sum[p*2+1]+f[p])%mod;
}
int main()
{
scanf("%d%d",&n,&m);
build(1,1,n);
k=1;
for (int i=1;i<=m;i++)
{
scanf("%d",&op);
if (op==1)
{
scanf("%d%d",&x,&y);
modify(1,1,n,x,y);
k=(LL)k*2%mod;
}
else printf("%lld\n",sum[1]);
}
}