啊我又来写这题惹。
还是一样的二分limit emmmm
然后知道了limit判断行不行(ok函数)
先把点都往上提。分成两类点:1.在limit范围内提得到根节点的。2.在limit范围内提不到根节点的。
(这里用倍增上提以优化复杂度,不赘述了)
提不到根节点的肯定是让他留在离根最近的点那边啊(毋庸置疑诶)
然后我们用vis给它标记一下,意思是vis以下的结点都ok的
而对于提的到根节点的军队,我们存一下它的结点号和到了根节点之后还剩下的距离(a数组)
现在我们要检查一下提完后还有啥路径没有被覆盖到的,同样存一下节点号和到根的距离(b数组)注意对于每条路径这里只要找离根只有一条路的距离的点(显然这些点才是最优的)
如果check完了发现所有路径全被覆盖了,直接return 1
否则我们继续……
下面就是结点和军队的配对咯。
刚才忘说了,对于找到的每一个到的了根节点的点,我们倍增完了顺便用他跟新一下结点x的restmin(说白了就是和它在一条路径上就短的军队)
sort一下(大的配对大的,小的配对小的)emmmm这里的sort一定要从大到小不然只有80分。。。因为一定要先满足远的点再满足近的点啊。。。否则不是最优
一个个看b点,先找有没有和它一个路径且没用过的点,如果有直接标注用过并continue
不然将a数组的军队一个个扫描,看有木有能匹配的。如果没有直接返回0
全能匹配就check return 1啦!
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=500005;
int cnt=0;int n,m,na,nb;
int u[maxn*2],v[maxn*2],w[maxn*2],head[maxn],nxt[maxn*2];
int p[maxn][35];ll dis[maxn][35];int army[maxn],used[maxn],vis[maxn];
int rest[maxn],restmin[maxn];
struct node
{
int id,rest;
}a[maxn],b[maxn];//a是军队,b是点
void add_edge(int x,int y,int z)
{
cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;
nxt[cnt]=head[x];head[x]=cnt;
}
int cmp(node x,node y)
{
return x.rest>y.rest;
}
void find(int x,int fa)
{
p[x][0]=fa;
for(int i=head[x];i!=-1;i=nxt[i])
{
if(v[i]!=fa)
{
dis[v[i]][0]=w[i];
find(v[i],x);
}
}
}
void prework()
{
for(int j=1;j<=17;j++)
{
for(int i=1;i<=n;i++)
{
p[i][j]=p[p[i][j-1]][j-1];
dis[i][j]=dis[i][j-1]+dis[p[i][j-1]][j-1];
}
}
}
int checkok(int x,int fa)
{
int fflag=0,flag=1;
if(vis[x]) return 1;
for(int i=head[x];i!=-1;i=nxt[i])
{
if(v[i]==fa) continue;fflag=1;
if(!checkok(v[i],x))
{
flag=0;
if(x==1) {b[++nb].id=v[i];b[nb].rest=w[i];}//b是要匹配的点
else return 0;
}
}
if(!fflag) return 0;
return flag;
}
int ok(int limit)
{
na=nb=0;
for(int i=1;i<=n;i++) vis[i]=rest[i]=0;
for(int i=1;i<=m;i++) used[i]=0;
for(int i=1;i<=m;i++)
{
int x=army[i],num=0;
for(int j=17;j>=0;j--)
{
if(p[x][j]>1&&(num+dis[x][j])<=limit)
{
num+=dis[x][j];x=p[x][j];
}
}
if(p[x][0]==1&&num+dis[x][0]<=limit)
{
a[++na].rest=limit-num-dis[x][0];a[na].id=i;
if(!rest[x]||a[na].rest<restmin[x])
restmin[x]=a[na].rest,rest[x]=i;
}
else vis[x]=1;
}
if(checkok(1,0)) return 1;
sort(a+1,a+1+na,cmp);sort(b+1,b+1+nb,cmp);
int now=1;used[0]=1;
for(int i=1;i<=nb;i++)
{
if(!used[rest[b[i].id]]) {used[rest[b[i].id]]=1;continue;}
while(now<=na&&(used[a[now].id]||a[now].rest<b[i].rest)) ++now;//找匹配
if(now>na) return 0;used[a[now].id]=1;
}
return 1;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);add_edge(y,x,z);
}
find(1,0);prework();
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d",&army[i]);
int l=0,r=500001;int ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(ok(mid)) {r=mid-1;ans=mid;}
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}