[CF768G] The Winds of Winter

显然对于删去一个点之后形成的森林, 就是把最大的那棵树砍下来, 接到最小的树上.

设删去当前点删去后形成的所有树中最大的那一棵大小为\(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\n", ans[i]);
}

int main() {
	freopen("del.in", "r", stdin);
	freopen("del.out", "w", stdout);

	init();
	solve();

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}


posted @ 2019-01-27 00:56  Qrsikno  阅读(364)  评论(0编辑  收藏  举报