[hdu 6241 Color a Tree]二分+树形DP

[hdu 6241 Color a Tree]二分+树形DP

分类:Binary Search

1. 题目链接

[hdu 6241 Color a Tree]

2. 题意描述

一棵 n 个节点的有根树。现在对它进行染色。有A+B个限制条件。
其中有 A 个条件为,第xi个节点的子树下染色顶点数目必须大于或等于 yi ,有 B 个条件为,对于不在第xi个节点的子树下的顶点,染色顶点数目必须大于或等于 yi
问最少需要染多少个顶点。

3. 解题思路

首先,二分需要染色的顶点个数。
设当前二分的染色顶点个数为 num ,那么对于第二种限制条件,可以转化为第 xi 个节点的子树下染色顶点数目必须小于或等于 numyi 。那么,每次check的时候,只需要dfs求出每个节点为根节点的子树下最多、最少分别能染多少个节点。
这题思路好像并不是很难,但是Debug了好久惹。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int inf = 0x3f3f3f3f;
const ll infl = 0x3f3f3f3f3f3f3f3fLL;
template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }
void debug() { cout << endl; }
template<typename T, typename ...R> void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }

const int MAXN = 100005;
int n;
vector<int> G[MAXN];
int A[MAXN], B[MAXN], C[MAXN];
int siz[MAXN];

void dfs_pre(int u, int fa) {
    siz[u] = 1;
    int cnt = 0;
    for (auto& v : G[u]) {
        if (v == fa) continue;
        dfs_pre(v, u);
        siz[u] += siz[v];
        cnt += A[v];
    }
    umax(A[u], cnt);
}

void dfs_max(int u, int fa) {
    int cnt = 1;
    for (auto& v : G[u]) {
        if (v == fa) continue;
        dfs_max(v, u);
        cnt += C[v];
    }
    umin(C[u], cnt);
}

bool check(int x) {
    for (int i = 1; i <= n; ++i) {
        C[i] = x - B[i];
        if (C[i] < A[i] ||  A[i] > siz[i]) return false;
        umin(C[i], siz[i]);
    }
    dfs_max(1, 0);
    // for (int i = 1; i <= n; ++i) debug(i, A[i], C[i]);
    for (int i = 1; i <= n; ++i) if (C[i] < A[i]) return false;
    return C[1] >= x;
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    int u, v, T; cin >> T;
    while (T --) {
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            G[i].clear();
            A[i] = B[i] = 0;
        }
        for (int i = 2; i <= n; ++i) {
            scanf("%d %d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        int na; cin >> na;
        for (int i = 1; i <= na; ++i) {
            scanf("%d %d", &u, &v);
            umax(A[u], v);
        }
        int nb; cin >> nb;
        for (int i = 1; i <= nb; ++i) {
            scanf("%d %d", &u, &v);
            umax(B[u], v);
        }
        dfs_pre(1, 0);
        int lb = A[1], ub = n, md, ans = -1;
        while (lb <= ub) {
            md = (lb + ub) >> 1;
            if (check(md)) ans = md, ub = md - 1;
            else lb = md + 1;
        }
        printf("%d\n", ans);
    }
#ifdef ___LOCAL_WONZY___
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值