版权声明:欢迎大家转载,转载请注明出处 https://blog.csdn.net/hao_zong_yin/article/details/82703382
A限制用树形dp就可以做,B限制一开始想维护一下父亲,兄弟等信息,但怎么都维护不出来,然后突然想到可以二分答案x,每个节点只要有A的信息以及答案x就可以得到B的信息了,想到以后扫了一遍点,判了一下dp[i]+b[i]是否小于x,WA了...
这是因为节点a满足条件,节点b满足条件,但是节点ab不一定同时满足条件,因此我们应该讲刚才的做法转化一下,x-b[i]转化为i的子树的上限,这样每次二分的时候来一遍树形dp就可以做了
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e5 + 10;
int t, n;
vector<int> g[maxn];
int a[maxn], b[maxn], sz[maxn], down[maxn], up[maxn];
void dfs(int f, int u) {
sz[u] = 1;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (v == f) continue;
dfs(u, v);
sz[u] += sz[v];
}
}
bool judge(int f, int u) {
int D = 0, U = 1;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (v == f) continue;
if (!judge(u, v)) return false;
D += down[v];
U += up[v];
}
down[u] = max(down[u], D);
up[u] = min(up[u], U);
return down[u] <= up[u];
}
int binarysearch() {
for (int i = 1; i <= n; i++) down[i] = a[i];
int l = 0, r = n;
while (l <= r) {
int m = (l + r)>>1;
for (int i = 1; i <= n; i++) up[i] = min(sz[i], m - b[i]);
if (judge(0, 1) && down[1] <= m && m <= up[1]) r = m - 1;
else l = m + 1;
}
return l;
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) g[i].clear();
for (int i = 1; i <= n-1; i++) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++) a[i] = b[i] = 0;
int A;
scanf("%d", &A);
for (int i = 1; i <= A; i++) {
int x, y;
scanf("%d%d", &x, &y);
a[x] = max(a[x], y);
}
int B;
scanf("%d", &B);
for (int i = 1; i <= B; i++) {
int x, y;
scanf("%d%d", &x, &y);
b[x] = max(b[x], y);
}
dfs(0, 1);
bool ok = true;
for (int i = 1; i <= n; i++) {
if (a[i] > sz[i] || b[i] > n - sz[i]) { ok = false; break; }
}
if (!ok) { puts("-1"); continue; }
else printf("%d\n", binarysearch());
}
return 0;
}