E. Tree Shuffling(树上问题 + 贪心 )

题意: 给你一颗树,根节点是1,每个节点有一个代价a[i],我们每个点有一个原始数字b[i],还有一个目标数字c[i](b[i],c[i]∈[0,1]),你可以挑选一个节点,然后给这个节点的k个子节点(0<=k<=子树大小)的原始数字任意排序,这个操作的代价是k*a[i],现在要使所有节点的数字都转变成目标数字,最小代价是多少?


思路:由于需要让答案尽可能小,所以能想到很明显的一个贪心策略,每次肯定会选择花费最小的结点去改变他的子树结构。

所以有个想法是先DFS预处理其子树的情况之后,之后根据节点的权值大小进行排序,之后进行根据权值来枚举节点来改变其子树。但是发现如果子子树情况改变比较难更新上面结点的状态,于是这个想法破产。

当我们DFS的时候,如果我们发现当前结点的权值是此时最小的,那么我们就可以直接用当前权值对他的子树进行操作。其实就相当于一个DP,先向下推,处理出从上往下的最小值,每次都是从之前已经处理过的状态向上推。向上推的时候处理出处理过后的0和1的数量。之后仅在为当前最小权值节点进行更改即可。如果最后到上推到1节点发现,还有1和0剩余那么就是没有答案。

复制代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
typedef long long ll;
ll a[maxn], ans;
int b[maxn], c[maxn], cnt[maxn][2], id[maxn];
vector<int> G[maxn];

void DFS(int u, int fa, int minn) {
    minn = min(a[u], 1ll*minn);
    for (int i = 0; i < G[u].size(); ++ i) {
        int v = G[u][i];
        if (v == fa) continue;
        DFS(v, u, minn);
        cnt[u][0] += cnt[v][0];
        cnt[u][1] += cnt[v][1];
    }
    if (b[u] != c[u]) {
        if (b[u] == 1) cnt[u][1] ++;
        else cnt[u][0] ++;
    }
    if (minn == a[u]) {
        int minc = min(cnt[u][0], cnt[u][1]);
        ans += a[u] * minc * 2;
        cnt[u][0] -= minc;
        cnt[u][1] -= minc;
    }
}

int main() {
    int n; scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) {
        scanf("%lld%d%d", &a[i], &b[i], &c[i]);
    }
    for (int i = 1; i <= n-1; ++ i) {
        int u, v; scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    DFS(1, 0, 1e9);
    if (cnt[1][0] | cnt[1][1]) ans = -1;
    printf("%lld\n", ans);


    return 0;
}
复制代码

 

posted @   ViKyanite  阅读(91)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
主题色彩