题目大意
给出一棵 n n n个节点的有根树,其中 1 1 1为根结点。有 q q q个询问,询问给定两个整数 p p p和 k k k,问有多少个有序三元组 ( a , b , c ) (a,b,c) (a,b,c)满足:
- a , b , c a,b,c a,b,c为树上三个不同的点,且 a a a为 p p p号节点。
- a , b a,b a,b都是 c c c的祖先。
- a , b a,b a,b在树上的距离不超过 k k k。
n ⩽ 300000 n\leqslant 300000 n⩽300000, q ⩽ 300000 q\leqslant 300000 q⩽300000。
思路
容易发现
a
a
a的值是确定的(就是
p
p
p)。
定义以点
x
x
x为根的子树大小为
s
i
z
e
[
x
]
size[x]
size[x],点
x
x
x的深度为
d
e
p
[
x
]
dep[x]
dep[x]。然后下面就有两种思路:
思路1:
假定
a
,
b
a,b
a,b不变,考虑
c
c
c可能的位置。有下面两种情况:
- b b b在 a a a的上方,那么 c c c一定在 a a a的下方,即在以 a a a为根的子树内。此时 c c c对答案的贡献为 s i z e [ a ] − 1 size[a]-1 size[a]−1。而 b b b只能在 a a a到根节点的这条链上,可能的位置数为 min { d e p [ a ] − 1 , k } \min\{dep[a]-1,k\} min{dep[a]−1,k}。
- b b b在 a a a的下方,那么 c c c一定在 b b b的下方,同理,此时 c c c对答案的贡献为 s i z e [ b ] − 1 size[b]-1 size[b]−1。而 b b b需满足 0 < d e p [ b ] − d e p [ a ] ⩽ k 0<dep[b]-dep[a]\leqslant k 0<dep[b]−dep[a]⩽k,保证 b b b在 a a a的下方且距离不超过 k k k。
所以对于每一个可能的 b b b,所有的 c c c的贡献之和(因为 a a a是确定的,所以这就是最终答案)为
a n s = min { d e p [ a ] − 1 , k } ⋅ ( s i z e [ a ] − 1 ) + ( ∑ d e p [ b ] − d e p [ a ] ∈ ( 0 , k ] ( s i z e [ b ] − 1 ) ) = min { d e p [ a ] − 1 , k } ⋅ ( s i z e [ a ] − 1 ) + ( ∑ d e p [ x ] > d e p [ a ] ( s i z e [ x ] − 1 ) ) − ( ∑ d e p [ y ] > d e p [ a ] + k ( s i z e [ y ] − 1 ) ) \begin{aligned} ans&=\min\{dep[a]-1,k\}\cdot(size[a]-1)+\bigg(\sum_{dep[b]-dep[a]\in(0,k]}\bigg(size[b]-1\bigg)\bigg)\\ &=\min\{dep[a]-1,k\}\cdot(size[a]-1)+\bigg(\sum_{dep[x]>dep[a]}\bigg(size[x]-1\bigg)\bigg)-\bigg(\sum_{dep[y]>dep[a]+k}\bigg(size[y]-1\bigg)\bigg) \end{aligned} ans=min{dep[a]−1,k}⋅(size[a]−1)+(dep[b]−dep[a]∈(0,k]∑(size[b]−1))=min{dep[a]−1,k}⋅(size[a]−1)+(dep[x]>dep[a]∑(size[x]−1))−(dep[y]>dep[a]+k∑(size[y]−1))
其中
b
,
x
,
y
b,x,y
b,x,y都在以
a
a
a为根的子树内。
对于等式变换的解释:
b
b
b是在
a
a
a的下方且距离不超过
k
k
k的点,这些点可以通过容斥得到,用
a
a
a下方所有的结点
x
x
x减去距离超过
k
k
k的结点
y
y
y。
思路2:
假定
a
,
c
a,c
a,c不变,考虑
b
b
b可能的位置。
由于
a
a
a是
c
c
c的祖先,
c
c
c一定在以
a
a
a为根的子树内,然后
b
b
b就一定在从
c
c
c到根节点的这条链上。这条链被点
a
a
a分成了两段,
b
b
b在这两段链上能放的位置数分别为
min
{
d
e
p
[
c
]
−
d
e
p
[
a
]
−
1
,
k
}
\min\{dep[c]-dep[a]-1,k\}
min{dep[c]−dep[a]−1,k}和
min
{
d
e
p
[
a
]
−
1
,
k
}
\min\{dep[a]-1,k\}
min{dep[a]−1,k}。
对于所有
a
a
a下面的
c
c
c,所有的
b
b
b的贡献之和为
a
n
s
=
∑
c
(
min
{
d
e
p
[
c
]
−
d
e
p
[
a
]
−
1
,
k
}
+
min
{
d
e
p
[
a
]
−
1
,
k
}
)
=
(
∑
c
min
{
d
e
p
[
c
]
−
d
e
p
[
a
]
−
1
,
k
}
)
+
(
s
i
z
e
[
a
]
−
1
)
⋅
min
{
d
e
p
[
a
]
−
1
,
k
}
=
(
∑
d
e
p
[
c
]
⩽
d
e
p
[
a
]
+
k
+
1
(
d
e
p
[
c
]
−
d
e
p
[
a
]
−
1
)
)
+
(
∑
d
e
p
[
c
]
>
d
e
p
[
a
]
+
k
+
1
k
)
+
(
s
i
z
e
[
a
]
−
1
)
⋅
min
{
d
e
p
[
a
]
−
1
,
k
}
\begin{aligned} ans&=\sum_c\bigg(\min\{dep[c]-dep[a]-1,k\}+\min\{dep[a]-1,k\}\bigg)\\ &=\bigg(\sum_c\min\{dep[c]-dep[a]-1,k\}\bigg)+(size[a]-1)\cdot\min\{dep[a]-1,k\}\\ &=\bigg(\sum_{dep[c]\leqslant dep[a]+k+1}\bigg(dep[c]-dep[a]-1\bigg)\bigg)+\bigg(\sum_{dep[c]>dep[a]+k+1}k\bigg)+(size[a]-1)\cdot\min\{dep[a]-1,k\} \end{aligned}
ans=c∑(min{dep[c]−dep[a]−1,k}+min{dep[a]−1,k})=(c∑min{dep[c]−dep[a]−1,k})+(size[a]−1)⋅min{dep[a]−1,k}=(dep[c]⩽dep[a]+k+1∑(dep[c]−dep[a]−1))+(dep[c]>dep[a]+k+1∑k)+(size[a]−1)⋅min{dep[a]−1,k}
问题来了,怎么求这个
a
n
s
ans
ans?
两个式子里都有
min
{
d
e
p
[
a
]
−
1
,
k
}
⋅
(
s
i
z
e
[
a
]
−
1
)
\min\{dep[a]-1,k\}\cdot(size[a]-1)
min{dep[a]−1,k}⋅(size[a]−1),这一部分是可以直接根据给定的
p
,
k
p,k
p,k算出来的,可以不管。
式子里另外一堆
∑
\sum
∑,本人研究了两种做法:分块、离线+树状数组。实测树状数组跑得飞快;分块做法在洛谷上吸氧能过,但在NKOJ始终有一个点被卡。
主席树、长链剖分什么的不在NOIP (早就没了) 的考纲内,这里不谈。
对于思路1:
分块做法: 由于 b b b在 a a a的子树内,先将所有结点按照dfs序放入一个数组中,结点 x x x记录 d e p [ x ] dep[x] dep[x]和 s i z e [ x ] − 1 size[x]-1 size[x]−1。对于每一个块,另外维护一个按 d e p dep dep值排序的结点序列以及这个序列的 s i z e [ x ] − 1 size[x]-1 size[x]−1值的前缀和。问题就变成了在 a a a的子树所覆盖的区间内(除了 a a a本身)找出 d e p [ x ] dep[x] dep[x]大于某个常数的结点的 s i z e [ x ] − 1 size[x]-1 size[x]−1之和。区间两边直接暴力统计,区间中间可以在有序序列中二分查找第一个 d e p [ x ] dep[x] dep[x]大于该数的位置,用前缀和统计答案。
树状数组做法: 看到上面的“找出
d
e
p
[
x
]
dep[x]
dep[x]大于某个常数的结点的
s
i
z
e
[
x
]
−
1
size[x]-1
size[x]−1之和”,容易想到以
d
e
p
[
x
]
dep[x]
dep[x]为下标,以
s
i
z
e
[
x
]
−
1
size[x]-1
size[x]−1的和为值,建立树状数组。但是这样不能保证查询的
x
x
x都在
a
a
a的子树内 (主席树偷笑) 。
这里用到了某一道神题中的差分思想:将询问离线,在dfs的过程中统计每个结点的答案。当进入某个结点时,树状数组的每个位置有一个值;当离开这个结点时,树状数组的每个位置又有一个新值;这两个值之差就是以该结点为根的子树产生的贡献。
对于思路2:
分块做法: 同样按照dfs序询问区间,不过每个结点只需要记录 d e p dep dep值。同样二分查找第一个大于 d e p [ a ] + k + 1 dep[a]+k+1 dep[a]+k+1的位置,统计 d e p [ x ] dep[x] dep[x]的前缀和;不同的是还要统计这样的点 x x x的个数,这样才能知道应该减去多少个 d e p [ a ] + 1 dep[a]+1 dep[a]+1,加上多少个 k k k。
树状数组做法: 略。