题目大意:给一颗树,1号节点已经被染黑,其余是白的,两个人轮流操作,一开始B在1号节点,A选择k个点染黑,然后B走一步,如果B能走到A没染的节点则B胜,否则当A染完全部的点时,A胜。求能让A获胜的最小的k
首先易知B不会走回头路,走回头路代表自己毫无贡献还让A多染了两次色
所以B的路径就是从根到叶子节点
先二分一下答案转成判定性问题
然后TreeDP,f[i]表示当B现在在i时,需要提前染好i的子树(不包含i)中的多少个节点才能保证A获胜
例如一个节点下方有X个叶子节点,而A每次只能染K个,那就需要提前染好其中的X-K个才能保证A获胜
那么显然可以得到f[i]=max(∑(f[j]+1)-K,0) (j是i的儿子)
最后看f[1]是否=0就好了
注意这题有坑,n=1输出0
#include<iostream>
#include<cstdio>
#define N 300010
using namespace std;
int to[N<<1],nxt[N<<1],pre[N],cnt;
int fa[N];
void ae(int ff,int tt)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
pre[ff]=cnt;
}
int f[N];
int D;
void build(int x)
{
int i,j;
int tot=0;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(j==fa[x]) continue;
fa[j]=x;
build(j);
tot+=f[j]+1;
}
f[x]=max(tot-D,0);
}
int main()
{
int n;
scanf("%d",&n);
if(n==1) {puts("0");return 0;}
int i,j,x,y;
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
ae(x,y);ae(y,x);
}
int l=1,r=n-1;
while(l<r)
{
for(i=1;i<=n;i++) f[i]=0;
D=(l+r)>>1;
build(1);
if(f[1]==0) r=D;
else l=D+1;
}
printf("%d",l);
}