2022暑期牛客加赛场

"蔚来杯"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 ; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值