Codeforces Round #801 (Div. 2) - D2. Tree Queries (Hard Version)

树形DP

Problem - D2 - Codeforces

题意

\(n(1<=n<=2*10^5)\) 个结点的树,可任选 k 个观察点 \(x_1,x_2...x_k\),对于任意的某个点 t,可以分别给出到这 k 个观察点的距离(边权均为1)求 k 的最小值,使得任意两个结点的这 k 个距离不完全相同(即可以通过到这 k 个观察点的距离,区别出任意两个点)

思路

  1. 对于 D1 的 n <= 2000, 可枚举根,分别建树
  2. 如果根是一个观察点,那么对于任意一个子树 u,它的子树 \(v_i\) 中最多只能有一个子树中完全没有观察点(如果有两个子树内都没有观察点,那这两个子树将无法区分出来)
  3. 树形DP求出 \(f[u]\) :以 u 为根的子树内至少要有几个观察点,答案为 \(f[root]\) + 1,因为根一定是观察点
  4. 对于 \(n<=2*10^5\), 可换根dp,但比较复杂;画图一下可以看出(感觉比赛的时候打表找规律更好一点),如果以度为 >= 3 的为根,根这个观察点是不必要的,因此找一个度 >= 3 的dp即可,答案不用 + 1

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>

using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 2e5 + 10;
const int INF = 1e9;
vector<vector<int> > G(N);
int n;
int f[N];
void add(int u, int v)
{
	G[u].push_back(v);
}

void init()
{
	for (int i = 1; i <= n; i++)
		G[i].clear();
}

int dfs(int u, int fa)
{
	int sum = 0, k = 0;
	for (int v : G[u])
	{
		if (v == fa)
			continue;
		int t = dfs(v, u);
		if (!t) k++;
		sum += t;
	}
	return sum + max(k - 1, 0);
}
int solve()
{
	if (n == 1)
		return 0;
	int root, maxn = 0;
	for (int i = 1; i <= n; i++)
	{
		if (G[i].size() > maxn)
		{
			maxn = G[i].size();
			root = i;
		}
	}
	if (maxn <= 2)
		return 1;
	return dfs(root, -1);
}
int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n;
		init();
		for (int i = 1; i < n; i++)
		{
			int u, v;
			cin >> u >> v;
			add(u, v), add(v, u);
		}
		cout << solve() << endl;
	}
    return 0;
}
posted @ 2022-09-22 17:31  hzy0227  阅读(12)  评论(0编辑  收藏  举报