[BZOJ5072] 小A的树

设计状态(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;
}
原文地址:https://www.cnblogs.com/sdfzhsz/p/9819254.html