问题描述
zxa有一棵含有n个节点的无根树,包含(n−1)条无向边,点从1到n编号,定义每个点的度数为与这个点相连的边的数量,度数为1的节点被称作这棵树的叶子节点。 zxa想给每个节点设置它的好看度,好看度必须为正整数。他的无根树有m(1≤m≤n)个叶子节点,其中的k(1≤k≤m)个叶子节点的好看度已经确定,zxa只需要设置其他节点的好看度。 zxa很好奇,如果令每条边的难看度是这条边所连接的两个节点的好看度差值的绝对值,整棵树的难看度是所有边的难看度中的最大值,那么这棵树的难看度最小是多少,你能帮助他吗?
输入描述
第一行有一个正整数T,表示有T组数据。 对于每组数据: 第一行有两个正整数n和k,表示这棵树有n个节点,其中k个叶子节点的好看度已经确定。 接下来(n−1)行,每行有两个互异的正整数u和v,表示节点u和节点v之间有一条无向边。 接下来k行,每行有两个正整数u和w,表示节点u是叶子节点,而且它的好看度是w。 每一行相邻数字之间只有一个空格。 保证输入的边构成一棵树。 1≤T≤10,2≤n≤5⋅104,1≤k≤n,1≤u,v≤n,1≤w≤109
输出描述
对于每组数据,输出一行,包含一个非负整数,表示这棵树的难看度最小值。
输入样例
2 3 2 1 2 1 3 2 4 3 9 6 2 1 2 1 3 1 4 2 5 2 6 3 6 5 9
输出样例
3 1
Hint
如果你需要更大的栈空间,请使用#pragma comment(linker, "/STACK:102400000,102400000")并且选择使用C++提交你的代码。
分析:
这个题目可以直接二分答案,然后对于答案用树dp去检验是否可以。
如何去检验呢?
所有节点都有一个值域。除了题目给出好感度ai的节点其值域为[ai,ai]。其余值域都初始化为[0,INF].
然后用搜索递推出所有节点的值域,如果某个节点的值域为空集那么该答案就不可以。
代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#define N 50010
#define INF 1000000009
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
vector<int>edge[N];
int root;
long long l[N], r[N];
int visit[N];
int n, m, k;
void Init()
{
for (int i = 1; i <= n; i++)
{
if (!visit[i])
{
l[i] = 0;
r[i] = INF;
}
}
}
bool dfs(int now, int pre, int dis)
{
for (int i = 0; i < edge[now].size(); i++)
{
if (edge[now][i] == pre) continue; //子节点不能等于父节点
if (!dfs(edge[now][i], now, dis)) return false; //搜索子节点
long long ll = l[edge[now][i]] - dis;
long long rr = r[edge[now][i]] + dis; //得到由儿子递推过来的新值域
if (ll > r[now] || rr < l[now]) return false; //判断是否矛盾
r[now] = min(r[now], rr);
l[now] = max(l[now], ll); //更新当前结点新值域
}
return true;
}
int main()
{
int t;
cin >> t;
while (t--)
{
scanf("%d%d", &n, &k);
for (int i = 1; i < N; i++)
edge[i].clear();
memset(visit, 0, sizeof(visit));
int u, v, w;
for (int i = 0; i < n - 1; i++)
{
scanf("%d%d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
for (int i = 0; i < k; i++)
{
scanf("%d%d", &u, &w);
l[u] = w;
r[u] = w;
visit[u] = 1;
}
int L = 0, R = INF, MID;
while (L <= R)
{
Init();
MID = (L + R) >> 1;
if (dfs(1, -1, MID)) R = MID - 1;
else L = MID + 1;
}
printf("%d\n", L);
}
return 0;
}