标签:树上dp
原题链接(其实并没有什么用)
考虑到q比较庞大(10^5级别),我们不能用单个处理询问的方法来解决,这道题的方法是用树上dp来做。我们令f[i][j][0]表示在以i点为根,有j个黑点时白点的最大数量,同理,f[i][j][1]表示在以i点为根,有j个黑点时白点的最小数量。我们不难得到转移方程:
f[x][i+j][0]=min(f[x][i][0]+f[o][j][0])
f[x][i+j][1]=max(f[x][i][1]+f[o][j][1])
注:x表示当前节点,o枚举x所有的儿子,用邻接表实现。
有一个细节问题,i,j都要倒序枚举,否则一个儿子节点会被重复计算。
时间复杂度为O(n^3+n*q),详见代码。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 805
#define INF 1000000000
#define add(u,v) (to[++top]=head[u],head[u]=top,w[top]=v)
#define For(x) for(int h=head[x],o=w[h];h;o=w[h=to[h]])
using namespace std;
int n,i,j,q,x,y,to[maxn],head[maxn],w[maxn],f[maxn][maxn][2],top=0;
bool vis[maxn],c[maxn],ff;
inline void dfs(int x)
{
vis[x]=1; f[x][c[x]][0]=f[x][c[x]][1]=1-c[x];
For(x)
if (!vis[o])
{
dfs(o);
for (i=n;i>=0;i--) if (f[x][i][0]!=INF)
for (j=n;j>=0;j--) if (f[o][j][0]!=INF)
f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[o][j][0]);
for (i=n;i>=0;i--) if (f[x][i][1]!=-1)
for (j=n;j>=0;j--) if (f[o][j][1]!=-1)
f[x][i+j][1]=max(f[x][i+j][1],f[x][i][1]+f[o][j][1]);
}
}
int main()
{
scanf("%d%d",&n,&q);
for (i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
for (i=1;i<=n;i++) scanf("%d",&c[i]);
for (i=1;i<=n;i++)
for (j=0;j<=n;j++) f[i][j][0]=INF,f[i][j][1]=-1;
memset(vis,0,sizeof(vis));
dfs(1);
while (q--)
{
scanf("%d%d",&x,&y);
ff=0;
for (i=1;i<=n;i++)
if (f[i][y][0]<=x-y&&x-y<=f[i][y][1])
{
ff=1; break;
}
if (ff) printf("Yes\n");else printf("No\n");
}
return 0;
}