题目意思就是在一棵树中所有点标记为两种颜色(黑和白)
然后询问是否存在大小为X恰好有Y个黑点的连通块
这题我们可以用树型背包的方法
设$f[i][j][0]$为以$i$为根的子树中大小为$j$的连通块的黑点数目的最小值,该连通块必须经过$i$
$f[i][j][1]$为以$i$为根的子树中大小为$j$的连通块的黑点数目的最大值,该连通块必须经过$i$
那么转移的时候有
$f[x][i + j][0] = min(f[x][i + j][0], f[x][i][0] + f[u][j][0]);$
$f[x][i + j][1] = max(f[x][i + j][1], f[x][i][1] + f[u][j][1]);$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 5010; int f[N][N][2], sz[N]; vector <int> v[N]; int T, n, q; int c[N], F[N], G[N]; void dfs(int x, int fa){ sz[x] = 1; if (c[x]) f[x][1][0] = f[x][1][1] = 1; else f[x][1][0] = f[x][1][1] = 0; for (auto u : v[x]){ if (u == fa) continue; dfs(u, x); dec(i, sz[x], 1){ rep(j, 1, sz[u]){ f[x][i + j][0] = min(f[x][i + j][0], f[x][i][0] + f[u][j][0]); f[x][i + j][1] = max(f[x][i + j][1], f[x][i][1] + f[u][j][1]); } } sz[x] += sz[u]; } rep(i, 1, sz[x]){ F[i] = min(F[i], f[x][i][0]); G[i] = max(G[i], f[x][i][1]); } } int main(){ scanf("%d", &T); while (T--){ scanf("%d%d", &n, &q); rep(i, 0, n) v[i].clear(); memset(sz, 0, sizeof sz); rep(i, 0, n) rep(j, 0, n) f[i][j][0] = 1 << 27, f[i][j][1] = 0; rep(i, 0, n) F[i] = 1 << 27, G[i] = 0; rep(i, 1, n - 1){ int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } rep(i, 1, n) scanf("%d", c + i); dfs(1, 0); for (; q--; ){ int x, y; scanf("%d%d", &x, &y); puts(F[x] <= y && G[x] >= y ? "Yes" : "No"); } } return 0; }