【LOJ2330】「清华集训 2017」榕树之心 - 题解

题目链接

「清华集训 2017」榕树之心

做法

考虑只询问 $ 1 $ 号节点怎么做。假如我们选择一个子树然后再选择另一个子树,那么这次操作就抵消了。
对于每一个节点,将它每一个子树的大小计为 $ a_i $ ,然后每次选择 $ a_x, a_y ( a_x > 0, a_y > 0 ) $ ,使 $ --a_x, --a_y $ ,使 $ \sum{a_i} $ 最小。最后有两种情况:

  1. 当 $ Max { a_i } \times 2 >= \sum{a_i} $ ,则该节点子树贡献为 $ Max { a_i } \times 2 - \sum{a_i} $ 。
  2. 否则每次去两个最大值消耗,该节点子树贡献为 $ ( \sum{a_i} ) \mod 2 $ 。

然后就可以在节点不同的儿子中内部抵消,就可以进行树形 DP ,记录一个节点儿子子树消耗后最大值、次大值以及该点子树消耗后的权值。
询问所有点则可以用用类似换根 DP 的方法。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fst first
#define snd second
using namespace std;
typedef pair<int, int> pii;
const int N = 100010, INF = 0x3f3f3f3f;
int W, T;
int n, ans[N], dep[N];
vector<int> e[N];
pii f[2][N], a[N];
// f[0][x] > f[1][x]

template<typename T>
inline T Max(const T &x, const T &y) { return x > y ? x : y; }
inline bool operator<(const pii &x, const pii &y) {
    return x.fst == y.fst ? x.snd > y.snd : x.fst < y.fst;
}
void dfs1(int u, int ff) {
    a[u].fst = 1, dep[u] = dep[ff] + 1;
    for(auto v : e[u]) if(v ^ ff) {
        dfs1(v, u), a[u].fst += a[v].fst;
        if(f[1][u] < a[v]) f[1][u] = a[v];
        if(f[1][u] > f[0][u]) swap(f[1][u], f[0][u]);
    }
    if(f[0][u].fst) {
        if(a[u].fst - f[0][u].fst - 1 >= f[0][u].snd + 1)
            a[u].snd = (a[u].fst - 1) & 1;
        else a[u].snd = f[0][u].snd + 1 - (a[u].fst - f[0][u].fst - 1);
    }
    else a[u].snd = 0;
}
void dfs2(int u, int ff, pii x) {
    pii U = Max(x, f[0][u]); int sz = n - dep[u] + 1;
    if(U.fst) {
        if(sz - U.fst - 1 >= U.snd + 1) ans[u] = (sz - 1) & 1;
        else ans[u] = U.snd + 1 - (sz - U.fst - 1);
    }
    for(auto v : e[u]) if(v ^ ff) {
        if(f[0][u] == a[v]) dfs2(v, u, Max(x, f[1][u]));
        else dfs2(v, u, Max(x, f[0][u]));
    }
}
int main() {
    scanf("%d%d", &W, &T);
    for(; T; --T) {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            e[i].clear(), f[0][i] = f[1][i] = a[i] = mp(0, INF);
        for(int i = 1, x, y; i < n; i++)
            scanf("%d%d", &x, &y), e[x].pb(y), e[y].pb(x);
        dfs1(1, 0), dfs2(1, 0, mp(0, INF));
        if(W == 3) printf("%d", ans[1] == 0);
        else for(int i = 1; i <= n; i++) printf("%d", ans[i] == 0);
        puts("");
    }
    return 0;
}

转载于:https://www.cnblogs.com/daniel14311531/p/10744884.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值