Codeforces Round #430 (Div. 2) C. Ilya And The Tree

题意 : 给你一棵树,一个点的美丽程度定义为根节点到这个节点的所有数的 gcd ,你可以将路径中的一个点删掉,求每个点最大的美丽程度是多少。

题解 : 注意到这个题gcd的递减速度是非常快的,每次有效的 gcd 最小下降 为 1 / 2,所以其收敛的速度是 logn 级别的,并且我们发现祖先节点的美丽程度一定大于其后代的美丽程度,又注意到每个点的美丽程度只与其祖先有关系,这是dfs 序 的天然条件,这样我们就可以dfs暴力枚举删掉哪一个数,不会超过 log 个节点。

坑点 : 如果数据中每个数都相等或者子孙都是祖先的倍数的话 gcd 不会收敛对于这种问题我们可以特殊处理一下就可以了。就像下面这样处理一下就可以了 。 

if (temp == pre) {

        if (pa == 1) {

            if (a[root] > pre) {

                res[root] = a[root];

            }

            else res[root] = pre;

        }

        else res[root] = pre;

    }



ac 代码 : 
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#define ll long long
using namespace  std;
const int maxn = 2e5 + 10;
int a[maxn] = {0};
vector <int> G[maxn];
vector <int> g[maxn];
vector <int> v;
bool vis[maxn] = {0};
int res[maxn] = {0};
int pos = 0;
int mx = 0;
int cnt = 0;
void dfs1 (int root) {
    int k = G[root].size();
    for (int i = 0;i < k; ++ i) {
        int u = G[root][i];
        if (!vis[u]) {
            g[root].push_back(u);
            vis[u] = 1;
            dfs1 (u);
        }
    }
}
void dfs2 (int pa,int root,int pre) {
    int k = g[root].size();
    if (!vis[root]){
        v.push_back (root);
        vis[root] = 1;
    }
    if (pre == 1) {
        if (pa == 1) {
            res[root] = a[root];
            for (int i = 0;i < k; ++ i) {
                int u = g[root][i];
                dfs2 (root,u,a[root]);
            }
        }
        else {
        res[root] = 1;
        for (int i = 0;i < k; ++ i) {
            int u = g[root][i];
            dfs2 (root,u,1);
            }
        }
        v.pop_back();
        return ;
    }
    int temp = __gcd(a[root],pre);
    if (temp == pre) {
        if (pa == 1) {
            if (a[root] > pre) {
                res[root] = a[root];
            }
            else res[root] = pre;
        }
        else res[root] = pre;
    }
    else {
    int y = v.size();
    int ans = 0;
    int sum = 0;
    for (int i = 0;i < y; ++ i) {
        sum = 0;
        for (int j = 0;j < y; ++ j) {
            if (j == i) continue;
            sum = __gcd(sum,a[v[j]]);
        }
        ans = max(ans,sum);
    }
    res[root] = ans;
    }
    for (int i = 0;i < k; ++ i){
        dfs2 (root,g[root][i],res[root]);
    }
    v.pop_back();
}
int main () {
    int n;
    scanf ("%d",&n);
    for (int i =1 ;i <= n ;++ i) scanf ("%d",a + i);
    for (int i = 0;i < n - 1; ++ i) {
        int x,y;
        scanf ("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    vis[1] = 1;
    dfs1 (1);
//    cout << g[2].size() << endl;
    res[1] = a[1];
    int k = g[1].size();
    memset (vis,0,sizeof (vis));
    for (int i = 0;i < k; ++ i) {
        int u = g[1][i];
        vis[1] = 1;
        v.push_back (1);
        dfs2 (1,u,a[1]);
        v.pop_back();
    }
    for (int i = 1;i <= n; ++ i) {
        printf ("%d ",res[i]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值