codeforces 1363E. Tree Shuffling(树上问题)

传送门

题意:

给出一棵树,每个点有三个值 a , b , c a,b,c a,b,c ,分别代表权值,当前的值,希望变成的值, b , c b,c b,c为0或1。

每次操作可以选择一个以 u u u根的子树,然后选择k个点, k k k任意,然后将这些点的 b b b值重排列,花费为 k × a [ u ] k \times a[u] k×a[u]

求最后所有点满足b都变为 c c c 的最小花费。

题解:

d p [ u ] [ 0 ] dp[u][0] dp[u][0] 表示以 u u u为根的子树内,有多少个点满足 b = 0 , c = 1 b=0,c=1 b=0,c=1 , d p [ u ] [ 1 ] dp[u][1] dp[u][1] 同理

那么当我们要对 u u u​ 的子树进行操作时,如果其某个祖先的花费小于 u u u ,那么其实可以选择祖先来进行操作

所以如果存在祖先的花费小,我们可以将 u u u的花费更改为祖先的花费。

再考虑重排列,对于一个点,只能选择 2 × m i n ( d p [ u ] [ 1 ] , d p [ u ] [ 0 ] ) 2 \times min(dp[u][1],dp[u][0]) 2×min(dp[u][1],dp[u][0]) 个点重排列,那么花费自然也就能算出来。

最后,因为每条链的操作其实是独立的,所以不用考虑当前点操作后,会对其他链上的点产生影响。

代码:

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=2e5+5;
const int inf=0x3f3f3f3f;
vector<int> g[MAXN];
int dp[MAXN][2];
int a[MAXN], b[MAXN], c[MAXN];
ll ans = 0;
void dfs(int u,int f)
{
    if(u!=1)
        a[u] = min(a[u], a[f]);
    dp[u][b[u]] += (b[u] != c[u]);
    for(auto v:g[u])
    {
        if(v==f)
            continue;
        dfs(v, u);
        dp[u][1] += dp[v][1];
        dp[u][0] += dp[v][0];
    }
    int mi = min(dp[u][1], dp[u][0]);
    ans += 1ll * mi*2 * a[u];
    dp[u][1] -= mi;
    dp[u][0] -= mi;
}
int main()
{
    int n;
    cin >> n;
    int cnt0 = 0, cnt1 = 0;
    for (int i = 1; i <= n;i++){
        cin >> a[i] >> b[i] >> c[i];
        if(b[i]==1)
            cnt0++;
        if(c[i]==1)
            cnt1++;
    }
    for (int i = 1; i <= n - 1;i++){
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    if(cnt0!=cnt1){
        cout << -1 << endl;
        return 0;
    }
    dfs(1,0);
    cout << ans << endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值