【牛客挑战赛】我是 A 题

在这里插入图片描述
题目还是有点坑的,我一开始以为删掉一些边,点也没了,不知道我是为什么会这么想…

其实这道题的意思是,给你一个树,让你删掉一些边,使得由树删掉边剩余的多个连通块每个连通块的权值和是k的倍数,求满足条件的剩余的边的最小权值和。注意还有一个条件:总的点权和就是k的倍数

当一道题没有思路的时候,我们考虑所有的可能的性质。

我们发现了一个性质:如果一个点的权值已经是k的倍数,我们可以直接把这条边删掉,也就是最小边权和不要这个点所链接的所有的边。这样剩下的点的权值和还是k的倍数(因为总的点权和就是k的倍数)。我们推广一下这个性质,也就是说所有权值和为k的倍数的连通块都可以不要,都可以单独隔开,也就是说这个权值和为k的倍数的连通块向外连的边我们都可以不要,所以我们可以直接dfs。维护一个当前连通块的权值和,最后回溯的时候,如果权值是k的倍数,我们就不要这个连通块的这条边,如果不是k的倍数,我们就脸上这个点,加上这个点的权值。最后总权值就是答案。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1e6 + 7, M = 5e6 + 7;

int n, m, k, tot;
int head[N], ver[M], nex[M];
ll edge[M];
ll node[N];
ll sum[N], res;
bool vis[N];

void add(int x, int y, int z)
{
    ver[tot] = y;
    edge[tot] = z;
    nex[tot] = head[x];
    head[x] = tot ++;
}

void dfs(int x, int fa, int edge_id)
{
    vis[x] = 1;
    for(int i = head[x];~i; i = nex[i]){
        int y = ver[i];
        if(y != fa && !vis[y]){
            dfs(y, x, i);
        }
    }
    if(node[x] % k == 0)return ;
    res += edge[edge_id];
    node[fa] += node[x];
}

int main()
{
    memset(head, -1, sizeof head);
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; ++ i)
        scanf("%lld", &node[i]);
    for(int i = 1; i <= n - 1; ++ i){
        int x, y;
        ll z;
        scanf("%d%d%lld", &x, &y, &z);
        add(x, y, z);add(y, x, z);
    }

    dfs(1, 1, 1);
    printf("%lld\n", res);
    return 0;
}

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页