Codeforces 1039D You Are Given a Tree
阿新 • • 發佈:2020-10-14
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\)
考慮對於 \(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; }