cf 1795F
明显二分答案,但是问题就在于验证函数怎么写。
目前的想法是树形dp,\(f[i][j]\)表示第i个节点作为的根的子树能否有从\(i\)出发长度为\(j\)的位置供我选择。
后效性处理。
很麻烦,不知道怎么搞。
也许是思路错了?但是二分答案应该是没有问题的,我觉得问题应该就是验证函数。
试试贪心。
从叶子开始往上,如果向下,也就是已经放好的地方能够放上,就直接放,这对上面的节点没有影响。
如果填补上就向上返回一个请求。要是一个节点有了两个请求就是寄了。
然后这个节点如果是有一个请求,就尝试让它往其他路放。
那对于一个请求就有两个处理方法,一个是向其他没有被占的子节点放,一个是向父亲节点放。
确实还是树形dp吧,额,没有状态,只是简单的遍历
没啥问题了。
就是初始化调了好久,我建了一个虚拟点,如何标记为n+2,忘记把这个点上面可能出现是数值清零了。。
导致我一直wa,然后又是小鸡帮我查的。哭了😭
这个贪心我其实不应该想这么久的,之前做过很多比这个更麻烦的换根dp,都没想的这坎坷。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char c=getchar();int a=0,b=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
int head[500001],tot,n,k,a[500001],b[500001];
const int inf=0x3f3f3f3f;
struct edge
{
int next,to;
}e[500001];
inline void add(int i,int j)
{
e[++tot].next=head[i];
e[tot].to=j;
head[i]=tot;
}
int cherk(int x,int fa,int length)
{
int ned=0,v,Max=0;
for(int i=head[x];i!=0;i=e[i].next)
{
int u=e[i].to;
if(u==fa)continue;
int v=cherk(u,x,length);
if(v<0)
if(ned==0)ned=-v;
else return inf;
else
if(v!=inf)
Max=max(Max,v);
else
return inf;
}
int mov=-1;
if(b[x])
{
mov=(length/k)+(b[x]<=length%k);
}
int top=Max;
if(x==n+2&&ned>0)return inf;
if(mov>=0&&ned!=0)return inf;
if(mov==-1&&ned==0)return top+1;
if(mov==-1)
{
if(top+1>=ned)return 0;
else return -ned+1;
}
if(ned==0)
{
if(top>=mov)return 0;
else return -mov;
}
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("2.out","w",stdout);
int T=read();
while(T--)
{
n=read();
for(int i=1;i<=tot;i++)head[i]=0,e[i].to=0,e[i].next=0;
tot=0;
for(int i=1;i<=n;i++)b[i]=0;b[n+2]=0;//¸ÃËÀµÄ³õʼ»¯
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
k=read();
for(int i=1;i<=k;i++)
{
a[i]=read();
b[a[i]]=i;
}
add(n+2,1);
int l=0,r=n;
while(l<r)
{
int mid=l+r>>1;
int x=cherk(n+2,0,mid);
if(x!=inf&&x>=0)l=mid+1;
else r=mid;
}
if(l!=0)l--;
cout<<l<<endl;
}
return 0;
}