倍增只是手段,重点是思维分析的过程。wuvin曾经说过,只要有好的思维,什么神题也打不倒我们。
军队可以移动,叶子节点不能被根节点到达,最短时间。
发现二分
首先发现时间越长,就越可能管辖成功。有这种显然的单调性促使我们二分找临界点。
对于一个限定的时间,我们来想想怎么判定是否合法。
发现越高越好
首先,我们发现,能管辖的越高越好,一次可以管辖更多的子树。所以我们可以尽力往上走。
但是有个问题:如果走到了根节点,那往哪里走呢?
不慌,遇事不决先存下来。不过走到根节点付出了一定时间,所以我们需要记录还能使用的时间和军队编号。
如果走不到?那就将这个最高能走到的地方打上一个标记,表示这个子树可以被管辖了。
在进行完这一步后,所有军队都要么到了1,要么到了自己能走的最高点。现在我们获得了一些可以从根节点出发的军队,和一个被部分管辖的树。
如果这个树已经被管辖了,那就可以宣告合法了。dfs一次查有没有被管辖。
如果自己被管辖或者自己的所有儿子被管辖都是合法的。
反之,从根节点出发,走一步就可以控制着一大坨儿子。那我们就不需要往下走。
遇事不决先存下来,将没有被完美管辖的根节点的儿子存下来,包括它们和根节点的距离。
然后问题变成了:有一些需要军队的子树,和一个剩余时间不一的军队集合。我们要分配这些军队。
很明显剩余时间长的去找远的子树,短的找近的,这样肯定最优。
但我们突然发现一个事情:如果一个军队本来可以轻松走到自己下面的一个子树做管辖,却一不小心走到了根节点,又走不回去了,这怎么办呢?
遇事不决先预处理。我们大可以在给每支军队往上走的时候,给所有根节点的子节点记录一个东西。这个东西是:从其子树上来的,能到根节点的,但剩余时间又最少的。
为什么最少呢?因为这样就可以减少损失。让最远的那个军队来占领它。
很明显,在使用当前剩余最大的那个剩余军队之前,如果能使用这个很小的,我们当然使用这个很小的。
那么就差不多了。
但是复杂度是n方logn的,我们来找优化。
很明显,只有在往上跳的这个过程可以简化。直接倍增就可以。
但是因为要处理每个根节点的子节点的最少剩余军队,所以倍增的时候不要直接跳到根。这样方便处理。
如此甚好,,差不多了。
#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}return cnt*f;
}
int n;
int first[100003],nxt[100003],to[100003],w[100003],tot;
void add(int a,int b,int c){
nxt[++tot]=first[a];first[a]=tot;to[tot]=b;w[tot]=c;
}
int m;int army[100003];
int fa[50003][17],dis[50003][17];
struct node{
int rest,id;
}a[50003];
struct bili{
int rest,id;
}b[50003];
int flag[50003],vis[50003],use[50003],cnta,cntb,minrest[50003];
void dfs0(int u,int faa){
fa[u][0]=faa;
for(int i=1;i<=16;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=1;i<=16;i++)dis[u][i]=dis[u][i-1]+dis[fa[u][i-1]][i-1];
for(int i=first[u];i;i=nxt[i]){
int v=to[i];if(v==faa)continue;dis[v][0]=w[i];dfs0(v,u);
}
}
bool dfs2(int u,int faa){
if(vis[u])return true;int flag=0,fg=1;
for(int i=first[u];i;i=nxt[i]){
int v=to[i];if(v==faa)continue;
flag=1;
if(!dfs2(v,u)){fg=0;
if(u==1){b[++cntb].rest=w[i];b[cntb].id=v;}
else return false;
}
}if(!flag)return false;return fg;
}
bool cma(node a,node b){
return a.rest>b.rest;
}
bool cmb(bili a,bili b){
return a.rest>b.rest;
}
bool check(int mid){
memset(flag,0,sizeof(flag));memset(vis,0,sizeof(vis));memset(use,0,sizeof(use));cnta=cntb=0;
for(int i=1;i<=m;i++){
int u=army[i],num=0;
for(int j=16;j>=0;j--){
if(fa[u][j]>1&&num+dis[u][j]<=mid){
num+=dis[u][j];u=fa[u][j];
}
}
if(fa[u][0]==1&&num+dis[u][0]<=mid){
a[++cnta].rest=mid-num-dis[u][0];a[cnta].id=i;
if(!flag[u]||a[cnta].rest<minrest[u]){
minrest[u]=a[cnta].rest;flag[u]=i;
}
}
else vis[u]=1;
}
if(dfs2(1,0))return true;
sort(a+1,a+cnta+1,cma);
sort(b+1,b+cntb+1,cmb);
int now=1;use[0]=1;
for(int i=1;i<=cntb;i++){
if(!use[flag[b[i].id]]){
use[flag[b[i].id]]=1;continue;
}
while(now<=cnta&&(use[a[now].id]||a[now].rest<b[i].rest))++now;
if(now>cnta)return 0;use[a[now].id]=1;
}return 1;
}
int main(){
n=in;
for(int i=1;i<n;i++){
int a=in;int b=in;int c=in;add(a,b,c);add(b,a,c);
}dfs0(1,0);
m=in;
for(int i=1;i<=m;i++)army[i]=in;
int l=0,r=500003;
while(l<r){
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}if(l==500003)cout<<"-1";else cout<<l;
return 0;
}