第二十一次CSP认证第五题:
给你n个三元组(x,y,z);进行m次操作;
每次操作:
1.给
[
l
,
r
]
[l,r]
[l,r]的三元组每个都加上
(
a
,
b
,
c
)
(a,b,c)
(a,b,c);
2.给
[
l
,
r
]
[l,r]
[l,r]的三元组每个都分别乘以
k
k
k;
3.对
[
l
,
r
]
[l,r]
[l,r]的三元组进行置换,即
(
x
,
y
,
z
)
(x,y,z)
(x,y,z)置换为
(
y
,
z
,
x
)
(y,z,x)
(y,z,x);
4.查询
[
1
,
n
]
[1,n]
[1,n]的三元组的和的模;
(
n
<
=
1
0
18
,
m
<
=
1
0
5
)
(n \ <=\ 10^{18} , m\ <=\ 10^5)
(n <= 1018,m <= 105)
分析:
这道题是一个非常经典的线段树;
这道题对于
80
%
80\%
80%的数据,是
n
<
=
1
0
6
n <= 10^6
n<=106的;
先对于
80
%
80\%
80%的数据考虑:首先对于加法和乘法,这个是非常经典的加法和乘法
t
a
g
tag
tag。对于剩下的置换,也是可以通过一个
t
a
g
tag
tag来解决的。
我们来好好分析加法
t
a
g
tag
tag和乘法
t
a
g
tag
tag的过程。
对于一段待处理的区间 [ l , r ] [l,r] [l,r],递归到当前层时,当前层的 t a g tag tag标记都已经传递到下面去了,当前这一层是没有 t a g tag tag的。也就是说,在递归进入这一层的时候,只需要处理自己这个操作的 t a g tag tag就行了。那 t a g tag tag之间的相互影响应该在什么时候处理呢?
应该在
p
u
s
h
d
o
w
n
pushdown
pushdown中处理。在
p
u
s
h
d
o
w
n
pushdown
pushdown操作中,是将当前层的
t
a
g
tag
tag传给自己的子区间。这里最重要的就是考虑一下运算的优先顺序。
我们令sum[node][0/1/2]表示
(
x
,
y
,
z
)
(x,y,z)
(x,y,z)的值,add[node][0/1/2],mul[node]分别表示加法tag和乘法tag,change[node]表示置换的次数(显然这里的置换次数是以3为周期的)。
这里change[node]会改变过去的mul[node]和add[node],而mul[node]会改变过去的add[node],add[node]不能改变其他的。
所以这里的tag的考虑顺序应该是change[node],mul[node],add[node];也就是在三个tag都存在的情况下,优先处理change[node],并将mul[node]和add[node]也改变。考虑mul[node]的时候也要改变add[node]。
而要完成这一题,还差一环,那就是在n很大的时候不能直接开点怎么办?这里就需要离散化,因为真正用到的点数只有 2 ∗ m 2*m 2∗m个,只需要将要用到的点离散化处理就可以了,计算的时候要记录每个点的真实坐标。
21.07.24补:
最近又写了一道线段树的tag题(hdu多校第二场的I love data structure),更深入的理解了lazy_tag的思想。
首先我们定义tag的优先级,假设
t
a
g
1
>
t
a
g
2
tag1>tag2
tag1>tag2,那么tag1的值变化的时候,tag2会受到影响,tag2变化的时候,tag1的值不会受到影响。对于CSP那题来说就是乘法tag变化的时候,会影响加法tag,加法tag变化的时候不会影响乘法tag。置换tag会影响加法和乘法tag,而加法、乘法tag变化的时候不会影响置换tag。所以优先级 置换tag > 乘法tag > 加法tag。
另外我们要明确,在tag是进行pushdown传递的,所以tag之间的相互影响必须在pushdown的时候解决,高优先级影响低优先级的tag。在pushdown后,所有的tag都是直接作用于线段树中原来的值,作用顺序是先进行高优先级操作,再进行低优先级操作(因为tag之间的相互影响已经处理)。
还有一种比较特殊的情况,那就是相同优先级,也就是两个tag会相互影响。
这个在2021hdu多校第二场的"I love data structure"中体现了。序列中每个点有个
(
a
i
,
b
i
)
(a_i, b_i)
(ai,bi),每次操作为:
- 给 [ l , r ] [l,r] [l,r]中的 a a a或者 b b b同时加上一个数
- 给 [ l , r ] [l,r] [l,r]中的 a a a和 b b b交换
- 将 [ l , r ] [l,r] [l,r]中的 a a a, b b b变成 3 a + 2 b 3a+2b 3a+2b和 3 a − 2 b 3a-2b 3a−2b
- 求 [ l , r ] [l,r] [l,r]中的 ∑ i = l i = r a i ∗ b i \sum_{i =l}^{i =r}{a_i * b_i} ∑i=li=rai∗bi
通过上面的分析,我可以发现,加法tag(tag1)不能影响任何一个tag的值,而交换tag(tag2)可以影响tag1,也可以影响变换tag(tag3);tag3也可以影响tag1和tag2。这就说明2,3操作在tag优先级上是相同的,只取决于两个操作的到达顺序。但是对于到达的顺序必须要保存下来,这样操作就必须用个vector存下顺序,或者用其他操作。
这道题巧妙的地方在于,使用了矩阵进行加速运算。用矩阵来代替vector,可以将运算速度提升为常数级别的。
定义矩阵:
X
=
(
a
b
)
\mathbf{X} = \left( \begin{array}{cccc} a\\ b\\ \end{array} \right)
X=(ab)
然后考虑转移矩阵:
S
w
a
p
=
(
0
1
1
0
)
\mathbf{Swap} = \left( \begin{array}{cccc} 0 & 1\\ 1 & 0 \\ \end{array} \right)
Swap=(0110)
c
h
a
n
g
e
=
(
3
2
3
−
2
)
\mathbf{change} = \left( \begin{array}{cccc} 3 & 2\\ 3 & -2 \\ \end{array} \right)
change=(332−2)