显然对于删去一个点之后形成的森林, 就是把最大的那棵树砍下来, 接到最小的树上.
设删去当前点删去后形成的所有树中最大的那一棵大小为(Max) , 最小的为Min, 次小值为Sec
设从最大树砍掉的节点为u, 答案就是(max{Max - Size_u, Min + size_u, Sec})
它关于(Size_u)变化的函数显然是单峰的, 所以可以二分答案(Ans)一定要满足(Max - Size_u leq Ans, Min + Size_u leq Ans)
就是: (Max - Ans leq Siz_u leq Ans - Min) , 只要找是否存在这样的点即可
这样处理的时候就很像DSU on tree, 每次暴力把轻儿子的贡献加入整体, 重儿子直接继承, 然后清空轻儿子的贡献.
讨论的时候要注意分三类讨论, 分成两大类, 分别是去掉这个点有无影响, 然后无影响又分为子树和其他部分.
时间复杂度是(O(n log^2 n log V)) 不知道为什么跑这么快
二分答案其实就是一个放宽答案取值区间的操作, 因为二分答案的时候保证了单调性, 所以可以直接计算考虑函数在定义域内的极值, 那么就转换成了最大/最小问题, 这也是他所以直接跟贪心相关.
那么就自然要求统计树上信息, 因为没有修改, 所以直接DSU on tree,
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
char ch = getchar();
int x = 0, flag = 1;
for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
const int Maxn = 500009;
struct edge {
int to, nxt;
}g[Maxn << 1];
int n, fa[Maxn], head[Maxn], e, rt = 0;
int ans[Maxn];
void add(int u, int v) {
g[++e] = (edge){v, head[u]}, head[u] = e;
}
int size[Maxn], son[Maxn];
int val[Maxn];
multiset <int> Ots, ancestor, Self[Maxn];
void dfsInit(int u) {
size[u] = 1;
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != fa[u]) {
dfsInit(v);
if (size[v] > size[son[u]]) son[u] = v;
size[u] += size[v];
}
}
if (u != rt) Ots.insert(size[u]);
}
void init() {
clar(head, -1);
n = read();
rep (i, 1, n) {
int u = read(), v = read();
fa[v] = u; add(v, u); add(u, v);
if (!u) rt = v;
}
dfsInit(1);
}
bool check(int opt, int val, int u, int Min, int Max) {
if (opt == 1) {
multiset <int> :: iterator IT = Self[son[u]].lower_bound(Max - val);
if (IT != Self[son[u]].end() && (*IT) <= val - Min) return true;
} else {
multiset <int> :: iterator IT = Ots.lower_bound(Max - val);
if (IT != Ots.end() && (*IT) <= val - Min) return true;
IT = ancestor.lower_bound(Max - val + size[u]);
if (IT != ancestor.end() && (*IT) <= val - Min + size[u]) return true;
}
return 0;
}
void dfs(int u) {
if (u != rt) Ots.erase(Ots.find(size[u]));
if (fa[u] && fa[u] != rt) ancestor.insert(size[fa[u]]);
int Fisr = max(n - size[u], size[son[u]]), Seco = min(n - size[u], size[son[u]]), Min = n - size[u];
if (!Min) Min = size[son[u]];
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != fa[u] && v != son[u]) {
dfs(v);
for (multiset <int> :: iterator IT = Self[v].begin(); IT != Self[v].end(); ++IT)
Ots.insert(*IT);
Min = min(Min, size[v]); Seco = max(Seco, size[v]);
}
}
if (son[u]) dfs(son[u]), Min = min(Min, size[son[u]]);
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != fa[u] && v != son[u])
for (multiset <int> :: iterator IT = Self[v].begin(); IT != Self[v].end(); ++IT)
Ots.erase(Ots.find(*IT));
}
if (Seco != Fisr) {
int l = Seco, r = Fisr, opt = (Fisr == size[son[u]]);
while (l <= r) {
int mid = (l + r) >> 1;
if (check(opt, mid, u, Min, Fisr)) ans[u] = mid, r = mid - 1;
else l = mid + 1;
}
}
if (!ans[u]) ans[u] = Fisr;
if (son[u]) swap(Self[son[u]], Self[u]);
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != fa[u] && v != son[u])
for (multiset <int> :: iterator IT = Self[v].begin(); IT != Self[v].end(); Self[v].erase(IT++)) Self[u].insert(*IT);
}
if (fa[u] && fa[u] != rt) ancestor.erase(ancestor.find(size[fa[u]]));
Self[u].insert(size[u]);
}
void solve() {
dfs(rt);
rep (i, 1, n) printf("%d
", ans[i]);
}
int main() {
freopen("del.in", "r", stdin);
freopen("del.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("
Running time: %.3lf(s)
", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}