Codeforces Round #359 (Div. 1) -B Kay and Snowflake

本文介绍了一种使用启发式合并和setsetset维护子树点的方法,以高效解答关于树重心的查询问题。通过预处理和优化,实现了对大量查询的快速响应。

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

洛谷传送门
BZOJ传送门

题目大意

给你一棵有 n n n个节点、 1 1 1号节点为根的无根树, q q q组询问, 每次询问一个点 x x x的子树内的重心是哪一个点。

输入输出格式

输入格式

第一行两个正整数 n , q n,q n,q

第二行 n − 1 n-1 n1个正整数 f i f_i fi, 表示 i + 1 i+1 i+1号点与 i i i号点有边。

以下 q q q行, 每行一个正整数 x x x, 表示一组询问。

输出格式

q q q行, 每行一个正整数, 表示对应询问的答案。

输入输出样例

输入样例#1:
7 4
1 1 3 3 5 3
1
2
3
5
输出样例#1:
3
2
3
6

解题分析

直接大力用 s e t set set维护每个子树中的点, 启发式合并即可。

查询 x x x时找到其 s i z ≥ ⌈ s i z x 2 ⌉ siz\ge \lceil\frac{siz_x}{2}\rceil siz2sizx的最小的一个子树即可。

当然也可以直接 O ( n ) O(n) O(n)做, 具体而言直接从某个 s i z > ⌈ s i z x 2 ⌉ siz>\lceil\frac{siz_x}{2}\rceil siz>2sizx的子树的重心开始向上枚举。 如果不存在这样的点那么重心就是当前点。(太懒了不写)

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cctype>
#include <vector>
#include <set>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 300500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n, cnt, q;
int ans[MX], siz[MX];
struct INFO {int siz, id;};
IN bool operator < (const INFO &x, const INFO &y)
{return x.siz < y.siz;}
std::multiset <INFO> st[MX];
std::vector <int> nex[MX], que[MX];
IN void add(R int from, R int to) {nex[from].push_back(to);}
void DFS(R int now, R int fa)
{
	siz[now] = 1; R int foo, bar;
	for (auto i : nex[now])
	{
		if (i == fa) continue;
		DFS(i, now);
		siz[now] += siz[i];
		foo = st[now].size(), bar = st[i].size();
		if (foo < bar)
		{
			for (auto j : st[now]) st[i].insert(j);
			st[now].clear(); st[now].swap(st[i]);
		}
		else
		{
			for (auto j : st[i]) st[now].insert(j);
			st[i].clear();
		}
	}
	int tar = (siz[now] + 1) >> 1, res;
	auto i = st[now].lower_bound({tar, 0});
	if (i == st[now].end()) res = now;
	else res = i -> id;
	for (auto i : que[now]) ans[i] = res;
	st[now].insert({siz[now], now});
}
int main(void)
{
	in(n), in(q); int foo;
	for (R int i = 2; i <= n; ++i) in(foo), add(i, foo), add(foo, i);
	for (R int i = 1; i <= q; ++i) in(foo), que[foo].push_back(i);
	DFS(1, 0);
	for (R int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值