设计状态(f[i][j])表示以i为根的子树,包含j个点的最小黑点数,(g[i][j])表示以(i)
为子树,包含(j)个点的最大黑点数,然后树形背包转移即可。
每次询问的时候就看包含x在不在(f[0][y])和(g[0][y])之间。
因为一个子图每删除一个点,再加入一个点,黑点个数的变化量不超过(1),所以一定有策略可以达到([min,max])中的每一个值.
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=5005;
int f[N][N],T,g[N][N],head[N],ecnt,col[N],siz[N],n;
struct Edge{int to,nxt;}e[N<<1];
inline void add(int bg,int ed) {e[++ecnt]=(Edge){ed,head[bg]};head[bg]=ecnt;}
inline void ins(int x,int y) {add(x,y);add(y,x);}
void dfs(int x,int fa) {
f[x][1]=g[x][1]=col[x];siz[x]=1;
for(int i=head[x];i;i=e[i].nxt) {
int v=e[i].to;
if(v!=fa) dfs(v,x);
else continue;
for(int j=siz[x];j;j--) {
for(int k=siz[v];k;k--) {
f[x][j+k]=min(f[x][j+k],f[x][j]+f[v][k]);
g[x][j+k]=max(g[x][j+k],g[x][j]+g[v][k]);
}
}
siz[x]+=siz[v];
}
for(int i=1;i<=n;i++) f[0][i]=min(f[0][i],f[x][i]),g[0][i]=max(g[0][i],g[x][i]);
}
int main() {
scanf("%d",&T);
while(T--) {
memset(f,0x3f,sizeof f);
memset(g,0xcf,sizeof g);
int q,x,y;
scanf("%d%d",&n,&q);
ecnt=0;memset(head,0,sizeof head);
for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),ins(x,y);
for(int i=1;i<=n;i++) scanf("%d",&col[i]);
dfs(1,0);
while(q--) {
scanf("%d%d",&y,&x);
if(f[0][y]<=x&&x<=g[0][y]) puts("YES");
else puts("NO");
}
}
return 0;
}