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