小 A 成为了一个园艺家!他有一棵 n 个节点的树(如果你不知道树是什么,请 看 Hint 部分)。他不小心打翻了墨水瓶,使得树的一些节点被染黑了。 小 A 发 现这棵染黑了的树很漂亮,于是想从树中取出一个 x 个点的联通子图,使得这 些点中恰有 y 个黑点,他想知道他的愿望能否实现。 可是他太小,不会算,请你帮帮他。
可以发现同样多的黑点所在子图的大小是一段连续区间,然后做法同农夫的钱
#include<bits/stdc++.h> using namespace std; int t ,n,q,a[5001],sz[5001],f[5001][5001],g[5001][5001],ff[5001],gg[5001]; vector<int> e[5001]; void dfs(int u,int fa){ int i,j,k; sz[u]=1;f[u][a[u]]=g[u][a[u]]=1; for(i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==fa)continue; dfs(v,u); memcpy(ff,f[u],sizeof(f[u])),memcpy(gg,g[u],sizeof(g[u])); for(j=sz[u];j>=a[u];j--){ for(k=sz[v];k>=a[v];k--){ ff[j+k]=max(ff[j+k],f[u][j]+f[v][k]); gg[j+k]=min(gg[j+k],g[u][j]+g[v][k]); } } sz[u]+=sz[v]; for(j=a[u];j<=sz[u];j++)f[u][j]=ff[j],g[u][j]=gg[j]; } for(i=0;i<=sz[u];i++)f[0][i]=max(f[0][i],f[u][i]),g[0][i]=min(g[0][i],g[u][i]); return ; } int main(){ memset(g,0x3f,sizeof(g)); cin>>t; while(t--){ scanf("%d%d",&n,&q); memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); int i; for(i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); e[x].push_back(y),e[y].push_back(x); } for(i=1;i<=n;i++)scanf("%d",&a[i]); dfs(1,0); while(q--){ int x,y; scanf("%d%d",&x,&y); if(g[0][y]<=x&&f[0][y]>=x)puts("YES"); else puts("NO"); } } return 0; }