cf768G The Winds of Winter 主席树

本文探讨了一道复杂的树形动态规划问题,利用主席树进行优化查询,以解决子树大小调整后的最大森林问题。文章详细介绍了算法思路,包括如何处理子树大小的查询,以及如何维护次大值以更新答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为啥beginend要拉我做这题呢(
因为他在cf找题的时候突然发现
在这里插入图片描述
秋膘-67??????x(当众处刑
然后我们就来大力刷了一下这题
这题就是给你一棵树,对于每个点,每次删掉这个点,删除之后树变成了一个森林,然后我们可以做一次把这个森林里面的某棵树的某个子树与父亲分开,然后把这个子树接到森林里其他树上的操作,问操作完之后的(可以不操作)森林里面树大小的最大值最小是多少

大概想想就是切开这个点,然后把最大的子树中的某个子树接到最小的子树上。
那么显然就是取最接近(max−min)2\frac{(max-min)}{2}2(maxmin)大小的子树就是最好的了
然后问题变成了寻找一个子树里面所有可能的子树大小中,最接近这个值的是多少
那如果最大的子树是当前点的子树一切好说,直接在dfs序的一段中用主席树查找就好了
但是如果是父亲那边的子树怎么办呢
那就比较迷幻了
然后beginend大爷又会了,然后告诉我,你可以用一个全局的主席树,减去父亲到根这条链的(因为这条链上实际子树大小要减去当前点的子树大小),然后再减去当前点的子树,在这里面查一次,然后在父亲到根这条链上再单独查一次就好了。
注意要维护次大值,因为次大值的子树大小可能成为新的答案。

/* ***********************************************
Author        :BPM136
Created Time  :2019/8/12 20:16:06
File Name     :G.cpp
************************************************ */

#include <bits/stdc++.h>
#include <sys/timeb.h>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
#define filein(x) freopen(#x".in","r",stdin)
#define fileout(x) freopen(#x".out","w",stdout)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#define mkd(x) freopen(#x".in","w",stdout);

using namespace std;

int random(int l, int r) {
	static std::random_device rd;
	struct timeb timeSeed;
	ftime(&timeSeed);
	size_t seed = timeSeed.time * 1000 + timeSeed.millitm;  // milli time
	static std::mt19937 gen(seed);
	std::uniform_int_distribution<> u(l, r);
	return u(gen);
}

typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int const N = 100005;

struct segnode {
	int lc, rc, s;
} t[N * 120];
int segcnt = 0;
int faroot[N], root[N];
int ver[N], cnt = 0;
int be[N], en[N], sz[N], fa[N];
pii mx[N], mi[N], se[N];
int n;

vector<int> G[N];

inline void addedge(int x, int y) {
	G[x].push_back(y);
}

void add(int& k, int lk, int l, int r, int pos, int val) {
	k = ++segcnt;
	t[k] = t[lk];
	t[k].s += val;
	if (l == r) 
		return;
	int mid = (l + r) >> 1;
	if (pos <= mid)
		add(t[k].lc, t[lk].lc, l, mid, pos, val);
	else
		add(t[k].rc, t[lk].rc, mid + 1, r, pos, val);
}

int queryrig(int k1, int k2, int k3, int k4, int l, int r, int ql, int qr) { //op : 1 right, 0 left
	// cerr << t[k1].s << ' ' << t[k2].s << ' ' << t[k3].s << ' ' << t[k4].s << ' ' << l << ' ' << r << '\n';
	if (ql <= l && r <= qr) {
		if (l == r) {
			int num = t[k1].s - t[k2].s + t[k3].s - t[k4].s;
			assert(num >= 0);
			if (num == 0)
				return -1;
			return l;
		}
		int mid = (l + r) >> 1;
		int num = t[t[k1].rc].s - t[t[k2].rc].s + t[t[k3].rc].s - t[t[k4].rc].s;
		assert(num >= 0);
		if (num) 
			return queryrig(t[k1].rc, t[k2].rc, t[k3].rc, t[k4].rc, mid + 1, r, ql, qr);
		else
			return queryrig(t[k1].lc, t[k2].lc, t[k3].lc, t[k4].lc, l, mid, ql, qr);
	}
	int mid = (l + r) >> 1, ret = -1;
	if (ql <= mid) 
		ret = max(ret, queryrig(t[k1].lc, t[k2].lc, t[k3].lc, t[k4].lc, l, mid, ql, qr));
	if (qr > mid)
		ret = max(ret, queryrig(t[k1].rc, t[k2].rc, t[k3].rc, t[k4].rc, mid + 1, r, ql, qr));
	return ret;
}

int querylef(int k1, int k2, int k3, int k4, int l, int r, int ql, int qr) { //op : 1 right, 0 left
	if (ql <= l && r <= qr) {
		if (l == r) {
			int num = t[k1].s - t[k2].s + t[k3].s - t[k4].s;
			if (num == 0)
				return n + 1;
			return l;
		}
		int mid = (l + r) >> 1;
		int num = t[t[k1].lc].s - t[t[k2].lc].s + t[t[k3].lc].s - t[t[k4].lc].s;
		if (num == 0)
			return querylef(t[k1].rc, t[k2].rc, t[k3].rc, t[k4].rc, mid + 1, r, ql, qr);
		else
			return querylef(t[k1].lc, t[k2].lc, t[k3].lc, t[k4].lc, l, mid, ql, qr);
	}
	int mid = (l + r) >> 1, ret = n + 1;
	if (ql <= mid) 
		ret = min(ret, querylef(t[k1].lc, t[k2].lc, t[k3].lc, t[k4].lc, l, mid, ql, qr));
	if (qr > mid)
		ret = min(ret, querylef(t[k1].rc, t[k2].rc, t[k3].rc, t[k4].rc, mid + 1, r, ql, qr));
	return ret;
}

void prework(int x) {
	ver[++cnt] = x;
	be[x] = cnt;
	sz[x] = 1;
	auto tr = vector<pii>();
	for (auto y : G[x]) {
		if (y == fa[x])
			continue;
		fa[y] = x;
		prework(y);
		sz[x] += sz[y];
		tr.emplace_back(sz[y], y);
	}
	en[x] = cnt;
	if (n - sz[x])
		tr.emplace_back(n - sz[x], -1);
	sort(all(tr));
	mi[x] = tr[0];
	mx[x] = tr[SZ(tr) - 1];
	se[x] = SZ(tr) >= 2 ? tr[SZ(tr) - 2] : make_pair(-1, -1);
}

void dfs(int x) {
	add(faroot[x], faroot[fa[x]], 1, n, sz[x], 1);
	for (auto y : G[x]) {
		if (y == fa[x]) 
			continue;
		dfs(y);
	}
}

int main() {
	USE_CIN_COUT;
	int rt = 0;
	cin >> n;
	if (n == 1) {
		cout << 0 << '\n';
		return 0;
	}
	for (int i = 1; i <= n; ++i) {
		int x, y;
		cin >> x >> y;
		if (x == 0)
			rt = y;
		else {
			addedge(x, y);
			addedge(y, x);
		}
	}
	prework(rt);
	dfs(rt);
	for (int i = 1; i <= n; ++i) 
		add(root[ver[i]], root[ver[i - 1]], 1, n, sz[ver[i]], 1);
	for (int i = 1; i <= n; ++i) {
		if (sz[i] == 1) {
			cout << n - 1 << '\n';
			continue;
		}
		int pos = (mx[i].first - mi[i].first) / 2;
		if (pos == 0) {
			cout << mx[i].first << '\n';
			continue;
		}
		if (mx[i].second == -1) {
			int x = i;
			int t1 = queryrig(root[ver[n]], root[ver[en[x]]], root[ver[be[x]]], faroot[x], 1, n, 1, pos);
			int t2 = querylef(root[ver[n]], root[ver[en[x]]], root[ver[be[x]]], faroot[x], 1, n, pos + 1, n);
			int ans = min(max(mx[i].first - t1, mi[i].first + t1), max(mx[i].first - t2, mi[i].first + t2));
			if (pos + sz[x] <= n) {
				t1 = queryrig(faroot[fa[x]], 0, 0, 0, 1, n, 1, pos + sz[x]) - sz[x];
				if (t1 >= 0)
					ans = min(ans, max(mx[i].first - t1, mi[i].first + t1));
				if (pos + sz[x] < n) {
					t2 = querylef(faroot[fa[x]], 0, 0, 0, 1, n, pos + sz[x] + 1, n) - sz[x];
					if (t2 >= 0)
						ans = min(ans, max(mx[i].first - t2, mi[i].first + t2));
				}
			}
			if (se[i].first != -1)
				ans = max(ans, se[i].first);
			cout << ans << '\n';
		} else {
			int x = mx[i].second;
			int t1 = queryrig(root[ver[en[x]]], root[ver[be[x]]], 0, 0, 1, n, 1, pos); ////1 means right
			int t2 = querylef(root[ver[en[x]]], root[ver[be[x]]], 0, 0, 1, n, pos + 1, n); ////0 means left
			int ans = min(max(mx[i].first - t1, mi[i].first + t1), max(mx[i].first - t2, mi[i].first + t2));
			if (se[i].first != -1)
				ans = max(ans, se[i].first);
			cout << ans << '\n'; 
		}
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值