原题题面:https://ac.nowcoder.com/acm/contest/38727/H
题目大意:
给定包含n个节点的有根树,根节点为1,定义
f
(
x
)
=
∏
i
=
1
n
l
c
a
(
x
,
i
)
f(x)=\prod^n_{i=1}lca(x,i)
f(x)=i=1∏nlca(x,i)
有q次询问,每次询问给定x,求
f
(
x
)
f(x)
f(x)后缀0数量
(
n
,
q
n,q
n,q均不超过
1
e
5
1e5
1e5)
分析:
遇事不决先画图
由上图,可以发现,对于点
A
A
A,所有点与它的
l
c
a
lca
lca皆在
A
A
A到
r
o
o
t
root
root的路径中
所以可以考虑树形dp
因为
10
=
2
∗
5
10=2*5
10=2∗5,我们可以预处理每个节点
i
i
i的2和5数量 ,记为
t
w
o
[
i
]
,
f
i
v
e
[
i
]
two[i],five[i]
two[i],five[i]
定义
d
p
[
v
]
.
t
w
o
dp[v].two
dp[v].two为在点v时
l
c
a
lca
lca积中2的数量
d
p
[
v
]
.
f
i
v
e
dp[v].five
dp[v].five为在点v时
l
c
a
lca
lca积中5的数量
看图可知,
d
p
[
s
o
n
i
]
dp[son_i]
dp[soni]的状态是由它上一级的祖先状态的得到的
所以对于每一个非叶子节点
u
u
u的所有
s
o
n
i
son_i
soni
状态转移方程为:
d
p
[
s
o
n
i
]
.
t
w
o
=
d
p
[
u
]
.
t
w
o
−
s
i
z
e
[
v
]
∗
(
t
w
o
[
u
]
−
t
w
o
[
s
o
n
i
]
)
dp[son_i].two=dp[u].two-size[v]*(two[u]-two[son_i])
dp[soni].two=dp[u].two−size[v]∗(two[u]−two[soni])
d
p
[
s
o
n
i
]
.
f
i
v
e
=
d
p
[
u
]
.
f
i
v
e
−
s
i
z
e
[
v
]
∗
(
f
i
v
e
[
u
]
−
f
i
v
e
[
s
o
n
i
]
)
dp[son_i].five=dp[u].five-size[v]*(five[u]-five[son_i])
dp[soni].five=dp[u].five−size[v]∗(five[u]−five[soni])
对于每次询问的x,
a
n
s
x
=
m
i
n
(
d
p
[
x
]
.
t
w
o
,
d
p
[
x
]
.
f
i
v
e
)
ans_x=min(dp[x].two,dp[x].five)
ansx=min(dp[x].two,dp[x].five)
时间复杂度:
O
(
n
l
o
g
n
+
n
+
q
)
O(n logn+n+q )
O(nlogn+n+q),是绰绰有余的
代码:
#include<bits/stdc++.h>
#define LL long long
const int MXN=1e5+7;
using namespace std;
int two[MXN],five[MXN];
int n;
struct Node{
int Two,Five;
};
int siz[MXN];
Node dp[MXN];
vector<int> g[MXN];
void DFS_1(int now,int fa){
int v; siz[now]=1;
for(int i=0;i<g[now].size();i++){
v=g[now][i];
if(v==fa) continue;
DFS_1(v,now);
siz[now]+=siz[v];
}
}
void DFS_2(int now,int fa){
int v;
for(int i=0;i<g[now].size();i++){
v=g[now][i];
if(v==fa) continue;
dp[v].Two=dp[now].Two-siz[v]*two[now]+siz[v]*two[v];
dp[v].Five=dp[now].Five-siz[v]*five[now]+siz[v]*five[v];
DFS_2(v,now);
}
}
int main(){
int q,x,u,v;
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
for(int i=1;i<=n;i++){
int t=i;
while(t%2==0){
two[i]++;
t>>=1;
}
while(t%5==0){
five[i]++;
t/=5;
}
}
DFS_1(1,-1);
dp[1].Two=siz[1]*two[1];
dp[1].Five=siz[1]*five[1];
DFS_2(1,-1);
while(q--){
scanf("%d",&x);
printf("%d\n",min(dp[x].Two,dp[x].Five));
}
}