1. 程式人生 > 實用技巧 >Codeforces 1039D You Are Given a Tree

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;
}