HDU 6241 Color a Tree
Color a Tree
题目大意:一棵树,根为1.某些点有一些限制。限制A: 该$x_i$点子树染色点至少$y_i$个。 限制B: 该$x_i$点子树外染色至少$y_i$个。求最少染色点数。
首先是二分答案,二分染色的点数。check就是对于这个mid能否满足要求。
令$f1[i]$表示$i$为根的子树的最少被染色个数。
令$f2[i]$表示$i$为根的子树的最多被染色个数。
符合要求的应该是$f1[i]<=mid<=f2[i]$。
考虑到$[L,M]$与$[M+1,R]$这样的子树合并的时候要求和,考虑按dfs深度分化,或者bfs序。
$f2[i]$更新由子树和mid-子树外的点的个数决定。
$f2[i]=min(\sum son,mid-bb[i])$.
$f1[i]$更新由子树和和子树内的点限制。
$f1[i]=max(\sum son,aa[i])$.
注意坑点 aa[i] 和 bb[i] 要取最大值。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
vector<int> e[maxn];
int fa[maxn];
int Q[maxn], T, f, r;
bool vis[maxn];
int aa[maxn], bb[maxn], na, nb, u, v, n;
int f1[maxn], f2[maxn], mid;
bool dfs(int id, int fa) {
f1[id] = 0; f2[id] = 1;
bool ok = 1;
for (auto ep : e[id]) {
if (ep == fa) continue;
ok &= dfs(ep, id);
if (!ok) break;
}
if (!ok) return false;
if (bb[id] != -1) {
if (mid - bb[id] < f1[id]) return false;
f2[id] = min(f2[id], mid - bb[id]);
}
if (aa[id] != -1) {
if (f2[id] < aa[id]) return false;
f1[id]=max(f1[id],aa[id]);
}
f1[fa] += f1[id];
f2[fa] += f2[id];
return true;
}
bool check(int mid) {
f1[0] = f2[0] = 0;
bool ok = dfs(1, 0);
if (!ok) return false;
if (f1[0] > mid) return false;
if (f2[0] < mid) return false;
return true;
}
int main() {
//freopen("in.txt","r",stdin);
for (cin >> T; T--;) {
scanf("%d", &n);
memset(aa, -1, sizeof aa);
memset(bb, -1, sizeof bb);
for (int i = 1; i <= n; i++)
e[i].clear();
for (int i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
scanf("%d", &na);
for (int i = 1; i <= na; i++) {
scanf("%d%d", &u, &v);
if (aa[u] == -1) aa[u] = v;
else aa[u] = max(aa[u], v);
}
scanf("%d", &nb);
for (int i = 1; i <= nb; i++) {
scanf("%d%d", &u, &v);
if (bb[u] == -1) bb[u] = v;
else bb[u] = max(bb[u], v);
}
int l = 1, r = n, res = -1;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid))
r = (res = mid) - 1;
else l = mid + 1;
}
printf("%d\n", res);
}
return 0;
}