题目大意:
给你一棵有
n
n
n个节点的树,每个点是黑点或白点。
q
q
q次询问,询问是否存在一个连通块满足大小为
x
i
x_i
xi,有恰好
y
i
y_i
yi个黑点。
n
≤
5000
n≤5000
n≤5000,
q
≤
1
0
5
q≤10^5
q≤105,
y
i
≤
x
i
≤
n
y_i≤x_i≤n
yi≤xi≤n
分析:
对于每一个点的为根的连通块进行讨论。
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为以
i
i
i为根的子树,有
j
j
j个黑点的连通块的最小大小。
设
g
[
i
]
[
j
]
g[i][j]
g[i][j]为以
i
i
i为根的子树,有
j
j
j个黑点的连通块的最大大小。
那么,以
i
i
i为根的包含
j
j
j个黑点连通块大小
∈
[
f
[
i
]
[
j
]
,
g
[
i
]
[
j
]
]
\in[f[i][j],g[i][j]]
∈[f[i][j],g[i][j]]。
而
f
,
g
f,g
f,g可以dp,使用枚举size法可以
O
(
n
2
)
O(n^2)
O(n2)算出。
考虑包含
x
x
x个黑点的连通块大小的取值范围,我们可以把上面的范围查分再跑前缀和就可以了。
代码:
/**************************************************************
Problem: 5072
User: ypxrain
Language: C++
Result: Accepted
Time:10000 ms
Memory:148344 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
const int maxn=5007;
const int inf=10000;
using namespace std;
int T,n,q,x,y,cnt;
int ls[maxn],size[maxn],a[maxn];
short f[maxn][maxn],g[maxn][maxn],ans[maxn][maxn],tmp1[maxn],tmp2[maxn];
struct edge{
int y,next;
}e[maxn*2];
void add(int x,int y)
{
e[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
short Min(short x,short y)
{
return x<y?x:y;
}
short Max(short x,short y)
{
return x>y?x:y;
}
void dfs(int x,int fa)
{
size[x]=1;
for (int i=0;i<=n;i++) f[x][i]=inf,g[x][i]=-inf;
f[x][a[x]]=1;
g[x][a[x]]=1;
for (int i=ls[x];i>0;i=e[i].next)
{
int y=e[i].y;
if (y==fa) continue;
dfs(y,x);
for (int j=0;j<=size[x]+size[y];j++) tmp1[j]=f[x][j],tmp2[j]=g[x][j];
for (int j=0;j<=size[x];j++)
{
for (int k=0;k<=size[y];k++)
{
tmp1[j+k]=Min(tmp1[j+k],f[x][j]+f[y][k]);
tmp2[j+k]=Max(tmp2[j+k],g[x][j]+g[y][k]);
}
}
size[x]+=size[y];
for (int j=0;j<=size[x];j++) f[x][j]=tmp1[j],g[x][j]=tmp2[j];
}
for (int i=0;i<=size[x];i++) if (f[x][i]<inf) ans[f[x][i]][i]++,ans[g[x][i]+1][i]--;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&q);
memset(ls,0,sizeof(ls));
cnt=0;
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(ans,0,sizeof(ans));
dfs(1,0);
for (int j=0;j<=n;j++)
{
for (int i=1;i<=n;i++) ans[i][j]+=ans[i-1][j];
}
for (int i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
if (ans[x][y]) printf("YES\n");
else printf("NO\n");
}
printf("\n");
}
}