这是哈尔滨的L题。只要想到二分答案就很水了。总数确定后每个子树有一个区间,树dp合并这个区间,一旦有空区间就不够。需要注意的是要特判下根节点的最大值是否能取到二分的答案。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=100007;
struct edge{
int to, next;
}e[maxn*2];
int head[maxn];
int cnt;
void init(){
cnt=0;
memset(head, -1, sizeof(head));
}
void add(int u, int v){
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
int n, a[maxn], b[maxn];
long long mn[maxn], mx[maxn];
bool flag;
void dfs(int u, int fa){
long long mnsum=0, mxsum=1;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(v==fa)continue;
dfs(v, u);
mnsum+=mn[v];
mxsum+=mx[v];
}
mn[u]=max(mn[u], mnsum);
mx[u]=min(mx[u], mxsum);
if(mn[u]>mx[u])flag=false;
}
bool check(int mid){
for(int i=1;i<=n;i++){
mn[i]=a[i], mx[i]=mid-b[i];
if(mn[i]>mx[i])return false;
}
flag=true;
dfs(1, 0);
if(mx[1]<mid)return false;
//若无特判下面数据会跪
//1
//3
//1 2
//1 3
//1
//1 1
//1
//1 1
return flag;
}
int main(){
int T;
scanf("%d", &T);
while(T--){
init();
scanf("%d", &n);
for(int i=1;i<n;i++){
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
int q;
scanf("%d", &q);
for(int i=1;i<=q;i++){
int id, num;
scanf("%d%d", &id, &num);
a[id]=max(a[id], num);
}
scanf("%d", &q);
for(int i=1;i<=q;i++){
int id, num;
scanf("%d%d", &id, &num);
b[id]=max(b[id], num);
}
if(!check(n)){
printf("-1\n");
}
else {
int l=0, r=n, tag=-1;
while(l<=r){
if(r-l<=1){
if(check(l))tag=l;
else tag=r;
break;
}
int mid=(l+r)/2;
if(check(mid))r=mid;
else l=mid;
}
printf("%d\n", tag);
}
}
}