zxa and leaf
Accepts: 25
Submissions: 249
Time Limit: 5000/2500 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
zxa有一棵含有nnn个节点的无根树,包含(n−1)(n-1)(n−1)条无向边,点从111到nnn编号,定义每个点的度数为与这个点相连的边的数量,度数为111的节点被称作这棵树的叶子节点。 zxa想给每个节点设置它的好看度,好看度必须为正整数。他的无根树有m(1≤m≤n)m(1\leq m\leq n)m(1≤m≤n)个叶子节点,其中的k(1≤k≤m)k(1\leq k\leq m)k(1≤k≤m)个叶子节点的好看度已经确定, zxa只需要设置其他节点的好看度。 zxa很好奇,如果令每条边的难看度是这条边所连接的两个节点的好看度差值的绝对值,整棵树的难看度是所有边的难看度中的最大值,那么这棵树的难看度最小是多少, 你能帮助他吗?
输入描述
第一行有一个正整数TTT,表示有TTT组数据。 对于每组数据: 第一行有两个正整数nnn和kkk,表示这棵树有nnn个节点,其中kkk个叶子节点的好看度已经确定。 接下来(n−1)(n-1)(n−1)行,每行有两个互异的正整数uuu和vvv,表示节点uuu和节点vvv之间有一条无向边。 接下来kkk行,每行有两个正整数uuu和www,表示节点uuu是叶子节点,而且它的好看度是www。 每一行相邻数字之间只有一个空格。 保证输入的边构成一棵树。 1≤T≤10,2≤n≤5⋅104,1≤k≤n,1≤u,v≤n,1≤w≤1091\leq T\leq 10,2\leq n\leq 5\cdot10^4,1\leq k\leq n,1\leq u,v\leq n,1\leq w\leq 10^91≤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++提交你的代码。
题解:
二分答案LLL,检查是否存在答案不超过LLL的解,也即对于任意一条边上的两个点uuu和vvv,有∣levelu−levelv∣≤L|level_u-level_v|\leq L∣levelu−levelv∣≤L,如果能维护出每个点可能的取值区间就可以判断是否有解了。
对于n>1n>1n>1的情况,随便找点做根,做树形dp即可,总的时间复杂度为O(nlogmax(w))O(n\log\max(w))O(nlogmax(w))。
觉得dfs容易爆栈的话可以设一个虚点做起点,连向已经确定levellevellevel的叶子,通过bfs确定一个拓扑序,在序上更新即可,时间复杂度同上。
Claris说随便选个点bfs就可以了。
这个题可以扩展,确定levellevellevel的点可以不是叶子,扩展版本用虚树做也是十分容易的。
思路:
二分答案+dfs求可行区间判断即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<vector>
#include<set>
#include<queue>
using namespace std;
const int N=5e4+9;
#define pb push_back
int n,T,root,val,k;
vector<int>g[N];
int w[N];
int minw[N],maxw[N];
bool dfs(int u,int fa)
{
minw[u]=1;
maxw[u]=1e9;
if(w[u])minw[u]=maxw[u]=w[u];
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==fa)continue;
if(!dfs(v,u))return 0;
minw[u]=max(minw[u],minw[v]-val);
maxw[u]=min(maxw[u],maxw[v]+val);
}
return minw[u]<=maxw[u];
}
bool ok(int m)
{
val=m;
return dfs(root,0);
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
int u,v;
for(int i=0;i<=n;i++)g[i].clear();
for(int i=0;i<n-1;i++){
scanf("%d%d",&u,&v);
g[u].pb(v);
g[v].pb(u);
}
if(n==2)root=1;
else for(int i=1;i<=n;i++)
if(g[i].size()>1){root=i;break;}
memset(w,0,sizeof(w));
for(int i=0;i<k;i++){
scanf("%d%d",&u,&v);
w[u]=v;
}
int l=0,r=1e9;
while(l<r){
int m=l+(r-l)/2;
if(ok(m))r=m;
else l=m+1;
}
printf("%d\n",l);
}
return 0;
}