Codeforces 1039D You Are Given a Tree

https://codeforces.com/contest/1039/problem/D

题意:给一颗 \(n\) 个节点的树,对 \(1\)\(n\) 所有的 \(i\),求 \(f_i\)

\(f_i\) 表示最多可以把树切分成多少个长度为 \(i\) 的链。\(n \le 10^5\)

题解:

考虑对一个确定的 \(i\) 如何求 \(f_i\),满足贪心性质,考虑某个节点如果在它的子树下有两条未取的链长度和(或者是一条就足够)不少于 \(i\),现在取掉这个点对子树外的答案没有影响,而且只能最多取一个,所以直接 \(f_u\) 表示 \(u\) 为根的子树 \(u\) 没有被取,子树内未取的最长链。贪心的每次满足就把答案增加并把当前子树的 \(f_u\)\(0\) 即可。复杂度 \(O(n)\)

考虑对于 \(i \le j\)\(f_i \ge f_j\),是很显然的规律,所以直接做整体二分即可。而且可以发现的是 \(f_{i+1} \le f_i \le \frac{n}{i}\) 所以取值也不超过 \(sqrt(n)\) 种,有很多段的值一样在整体二分中剪枝特判掉,总复杂度就可以达到 \(O(n\log n\log n)\)

取值的和为 \(O(\sum_{i=1}^{n} \frac{n}{i})=O(n\log n)\)

然后发现可能是由于递归深度会超时,用各种操作把递归改成循环即可(括号序列或者直接记录 dfs 路径)。

代码:

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/10/14 16:45
*
================================================================*/
#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC optimize ("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")

#define VI vector<int>
#define ll long long
using namespace std;

namespace IO {
    template<class T>
    void _R(T &x) { cin >> x; }
    void _R(int &x) { scanf("%d", &x); }
    void _R(ll &x) { scanf("%lld", &x); }
    void _R(double &x) { scanf("%lf", &x); }
    void _R(char &x) { x = getchar(); }
    void _R(char *x) { scanf("%s", x); }
    void R() {}
    template<class T, class... U>
    void R(T &head, U &... tail) {_R(head),R(tail...);}
    template<class T>
    void _W(const T &x) { cout << x; }
    void _W(const int &x) { printf("%d", x); }
    void _W(const ll &x) { printf("%lld", x); }
    void _W(const double &x) { printf("%.16f", x); }
    void _W(const char &x) { putchar(x); }
    void _W(const char *x) { printf("%s", x); }
    template<class T, class U>
    void _W(const pair<T, U> &x) {_W(x.F),putchar(' '),_W(x.S);}
    template<class T>
    void _W(const vector<T> &x) { for (auto i = x.begin(); i != x.end(); _W(*i++)) if (i != x.cbegin()) putchar(' '); }
    void W() {}
    template<class T, class... U>
    void W(const T &head, const U &... tail) {_W(head),putchar(sizeof...(tail) ? ' ' : '\n'),W(tail...);}
}
using namespace IO;


const int maxn = 1e5+50;
const int mod = 1e9+7;

ll qp(ll a, ll n) {
    ll res = 1;
    n %= mod - 1;
    if (n < 0) n += mod - 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

ll qp(ll a, ll n, int mod) {
    ll res = 1;
    n %= mod - 1;
    if (n < 0) n += mod - 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

int n;
int res[maxn], f[maxn], dfn[maxn], cnt;
VI edge[maxn], G[maxn];

inline void dfs(int u, int pre) {
    dfn[u] = ++cnt;
    for (auto v : edge[u]) {
        if (v != pre) {
            dfs(v, u);
            G[dfn[u]].push_back(dfn[v]);
        }
    }
}

inline int stkdfs(int k) {
    int res = 0;
    for (int i = n; i >= 1; --i) {
        int mx = 0, mxx = 0;
        for (auto v : G[i]) {
            if (f[v] > mx) mxx = mx, mx = f[v];
            else if (f[v] > mxx) mxx = f[v];
        }
        if (mx + mxx + 1 >= k) f[i] = 0, res ++;
        else f[i] = mx + 1;
    }
    return res;
}

inline void solve(int l, int r, int xl, int xr) {
    if (xl == xr) {
        for (int i = l; i <= r; ++i) res[i] = xl;
        return;
    }
    int mid = l + r >> 1;
    res[mid] = stkdfs(mid);
    if (l < mid) solve(l, mid - 1, res[mid], xr);
    if (r > mid) solve(mid + 1, r, xl, res[mid]);
}

int main(int argc, char* argv[]) {
    R(n);
    for (int i = 1; i < n; ++i) {
        int u, v;
        R(u, v);
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    dfs(1, 0);
    solve(1, n, 0, n);
    for (int i = 1; i <= n; ++i) W(res[i]);
    return 0;
}
posted @ 2020-10-14 18:44  badcw  阅读(125)  评论(0编辑  收藏  举报