"蔚来杯"2022牛客暑期多校训练营加赛
H-Here is an Easy Problem of Zero-chan
题目大意
给出 n n n个节点的一棵树,和 q q q个询问,对于每个询问个数的一个数 x x x,输出 f ( x ) f(x) f(x) , f ( x ) f(x) f(x) 表示这个节点与 1 1 1到 n n n当中每个点的最近公共祖先的点的编号的乘积的后缀零的个数
思路
首先,对于计算后缀零的个数,如果直接乘出这个数可能会爆 i n t int int,处理起来更加麻烦,所以可以改为求 质因数中 2 2 2 的个数以及 5 5 5 的个数,再在这两个数的数量上取个数更少的数就是后缀零的个数了。
模拟样例可以发现,对于每个节点 x x x,它的 f ( x ) f(x) f(x) 的计算可以分为两部分,一部分是以 x x x 为根的下面的点,和不以 x x x 为根的点。
对于第一部分,它们的最近公共祖先就是 x x x,这一部分的 2 2 2 和 5 5 5 的个数就是以 x x x 为根的子树中包含的点的个数(用 s z [ x ] sz[x] sz[x] 表示)乘以 x x x 中 2 2 2 和 5 5 5 的质因数的个数;这一部分是比较好求的。
对于第二部分,我们可以发现,当我们知道了 x x x 的父节点 f a fa fa 所对应的第二部分的 2 2 2 和 5 5 5 的个数时, x x x 的第二部分 2 2 2 和 5 5 5 的个数只需要在 f a fa fa 的基础上加上 x x x 与 x x x 的兄弟节点的最近公共祖先中包含 2 2 2 和 5 5 5 的个数,而他们的公共祖先又都是 f a fa fa,个数是 s z [ f a ] sz[fa] sz[fa] - s z [ x ] sz[x] sz[x] ,这里其实就是 d p dp dp 的状态转移过程,同时发现 1 1 1 作为根节点,这一部分的 2 2 2 和 5 5 5 的个数必定都是 0 0 0,也不需要刻意去初始化;
这样,先预处理出第一部分,再用树形dp处理出第二部分,最后比较 2 2 2 和 5 5 5 的个数,就完成了
代码
#include <bits/stdc++.h>
using namespace std ;
const int N = 1e5 + 10 , M = 2 *N ;
int h[N] , ne[M] , e[M] , idx ;
int sum5[N] , sum2[N] , cnt[2][N], sz[N];
int n , q ;
void init()
{
for(int i = 1 ; i<= n ;i ++)
{
int t = i;
if(t % 2 == 0)
{
int s =0 ;
while(t%2==0) t/= 2 , s++ ;
cnt[0][i] = s ;
}
t = i ;
if(t % 5 ==0 )
{
int s = 0 ;
while(t % 5 ==0) t/= 5 , s++ ;
cnt[1][i] = s ;
}
}
}
void add(int a, int b)
{
e[idx]= b , ne[idx] = h[a] , h[a] = idx++ ;
}
int dfs(int u , int fa)
{
sz[u] = 1 ;
for(int i = h[u] ; i != -1 ; i = ne[i])
{
int j = e[i] ;
if(j == fa) continue ;
sz[u] += dfs( j , u) ;
}
return sz[u] ;
}
void dfs1(int u , int fa)
{
for(int i = h[u] ; i!= -1 ; i = ne[i])
{
int j = e[i] ;
if(j == fa) continue ;
int ss = sz[u] - sz[j] ;
sum2[j] = sum2[u] + ss * cnt[0][u] ;
sum5[j] = sum5[u] + ss * cnt[1][u] ;
dfs1(j , u) ;
}
}
int main()
{
memset(h , -1 , sizeof h) ;
cin >> n >> q ;
init() ;
for(int i = 1 ; i<n ;i++)
{
int a ,b ;
cin >>a >>b ;
add(a ,b ), add(b, a ) ;
}
dfs(1 , -1) ;
dfs1(1 , -1 ) ;
for(int i = 1 ; i<= q ; i++)
{
int a ;
cin >> a ;
cout << min(sum2[a] + sz[a] * cnt[0][a] , sum5[a] + sz[a] * cnt[1][a]) << endl;
}
return 0 ;
}