[CF1796E] Colored Subgraphs 题解

题目大意

给你一棵树, n n n 个节点。你需要给每个节点染色,使得任意颜色相同的节点之间的路径上的点颜色也与前面两个点相同。

c n t i cnt_i cnti 表示颜色为 i i i 的节点数,找到 min ⁡ c n t i \min cnt_i mincnti 的最大值。

简要分析

先不考虑,根不固定的情况,强制钦定 r = 1 r = 1 r=1,这里 r r r 指根节点。

我们发现答案通过二分可以转化成判定性问题,即能否将树划分成若干条自底向上目长度至少为 k k k 的链。

d e p u dep_u depu 表示,自底向上的到达 u u u 的最短长度, m i n x minx minx 表示么 x x x 的儿子中 d e p v dep_v depv 的最小值。

显然对于叶子有, d e p w = 1 dep_w=1 depw=1,而对于非时子节点,显然有转移 d e p w = m i n x + 1 dep_w=minx +1 depw=minx+1

对于不合法的情况, u u u 存在两个儿子,他们的 d e p w dep_w depw 都要小于 k k k,因为 u u u 只能和一个儿子往上拼接,两个就不行了,这个记为情況 1 1 1

最终只需要判断一下 d e p 1 dep_1 dep1, 是否不小于 k k k 即可,这个记为情况 2 2 2

然后就是, r r r 不固定的情况,当然,假设说 r = 1 r = 1 r=1 可行那么我们就直接输出了。

如果情况 1 1 1 不合法,子树以外的点作为根是不可能的,因为这个子树的本身结构是不变的,所以我们就需要将 m i n x minx minx 对应的节点 v v v 变成根。

因为这种情况不合法一定是说明了这个点到 u u u 距离是小于 k k k 的,而肯定还有一个 v ’ v’ v u u u 的距离是小于 v v v 的,所以我们本质上是把 v v v v ’ v’ v 的路径涂成一种颜色。

如果情况 2 2 2 不合法,那么我们同理,也是找 m i n x minx minx 就行了。

总时间复杂度 O ( n ) O(n) O(n)

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>

using namespace std;

typedef long long ll;
const ll maxn = 2e5 + 7;
const ll INF = 1e18 + 7, MOD = 998244353;

inline ll read() {
    char cCc;
    ll xXx = 0, wWw = 1;
    while (cCc < '0' || cCc > '9')
        (cCc == '-') && (wWw = -wWw), cCc = getchar();
    while (cCc >= '0' && cCc <= '9')
        xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
    xXx *= wWw;
    return xXx;
}

inline void write(ll xXx) {
    if (xXx < 0)
        putchar('-'), xXx = -xXx;
    if (xXx > 9)
        write(xXx / 10);
    putchar(xXx % 10 + '0');
}

ll n, tx, ty, dep[maxn], minnode[maxn];
vector<ll> G[maxn];

bool dfs(ll u, ll fa, ll k) {
    ll x = 0, y = 0, miny = INF, minx = INF;
    for (auto v : G[u]) {
        if (v == fa)continue;
        if (!dfs(v, u, k))return false;
        if (dep[v] <= minx)y = x, miny = minx, x = minnode[v], minx = dep[v];
        else if (dep[v] <= miny)y = minnode[v], miny = dep[v];
    }

    if (miny < k) {
        tx = x, ty = y;
        return false;
    }

    if (x == 0)dep[u] = 1, minnode[u] = u;
    else dep[u] = minx + 1, minnode[u] = x;

    if (!fa) {
        if (dep[u] < k)tx = minnode[u], ty = 1;
        return dep[u] >= k;
    }

    return true;
}

bool check(ll k) {
    if (dfs(1, 0, k)) return true;
    else if (dfs(tx, 0, k) || dfs(ty, 0, k))return true;
    return false;
}

void solve() {
    n = read();
    for (ll i = 1; i < n; i++) {
        ll u = read(), v = read();
        G[u].push_back(v);
        G[v].push_back(u);
    }

    ll l = 0, r = n, ans = 0;
    while (l <= r) {
        ll mid = (l + r) >> 1;
        if (check(mid))
            ans = mid, l = mid + 1;
        else r = mid - 1;
    }

    cout << ans << '\n';
    for (ll i = 1; i <= n; i++)G[i].clear();
}

signed main() {
//    freopen("code.in","r",stdin);
//    freopen("code.out","w",stdout);
    ll T = read();
    while (T--)solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值