真是一道难题。观察到题目的时间这个量,时间越大显然覆盖的点越多,所以我们说答案具有单调性,这样便可以二分答案了。
接下来最关键的就是如何检验这一个答案,对于一个答案k:
如果有一些点往回移动k的距离仍然不能达到根节点,我们肯定要尽量的把它往上移,这里采用暴力即可。
有些点可以到根。好吧这个时候呢,把还没有控制的根的儿子节点们拿出来,把可以越过根且没有匹配的军队拿出来跑二分图匹配。复杂度O(nmlogn)显然TLE。
我们可以脑补一种贪心的匹配方法。记R[i]表示i这只军队到达根节点后还剩下的时间,B[j]表示j这个根结点的子节点(第二层节点)到根的时间。
那么一只军队能控制一个子节点当且仅当R[i]>=B[j]。这样的话军队移动的时间为k-(R[i]-B[j]),我们的任务是找到一种匹配方法,使得这个式子的最大值最小。
脑补一下便可发现R[i],B[j]越近越好,所以就两个数组排好序匹配啦,下面给出个人认为正确的证明:
如果一只军队不与他本应(指排好序的)那个数配对,那么R[i]-B[j]可能会变小,如果这次的差没变小也会引起别的i的差变小,所以这个式子的最大值变大。
事实真的如此简单吗?不
如果一只军队可以与自己那条路径上的那个第二层根节点(A)配对,且这个军队如果到达根便无法返回A,那么就让他驻扎在A。
证:如果这只军队去了别的地方x点,然后y军队来填补A,由已知得w(A)>W(x)显然让y去x,这支军队驻扎在A最好。
所以如果存在R[i]<B[j]且也能匹配到自己路上的那个点时,取R[i]值最小的匹配。剩下的实现自己去弄吧。感谢Dash的帮助。%%__debug大神。
#include<cstdio> #include<queue> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define x first #define y second #define pii std::pair<long long,int> const int MAXN=50005*2; int first[MAXN],next[MAXN],m,Militar[MAXN],n,fa[MAXN],isLeaf[MAXN]; int e=0,v[MAXN],hasf[MAXN],vis[MAXN],bel[MAXN],f[MAXN]; long long ww[MAXN],dis[MAXN],w[MAXN]; bool use[MAXN]; void add(int a,int b,int c) { e++;next[e]=first[a];first[a]=e;ww[e]=c;v[e]=b; } bool cmp(const pii& a,const pii& b) { return a.x<b.x; } void push_up(int u,int k) { int sum=0; vis[u]=1; while(1) { sum+=w[u]; if(sum>k)break; u=fa[u];if(u==-1)break; } vis[u]=1; } pii sons[MAXN]; pii R[MAXN]; void calc(int u,int fa,int vt) { if(hasf[u])bel[u]=vt; for(int i=first[u];i!=-1;i=next[i]) { int to=v[i]; if(to==fa)continue; calc(to,u,vt); } } bool dfs(int u,int fa) { if(isLeaf[u])return 1; for(int i=first[u];i!=-1;i=next[i]) { int to=v[i]; if(to==fa)continue; if(!vis[to]) { if(dfs(to,u))return 1; } } return 0; } void calc_vis(int u,int fa) { int flag=1; if(vis[u])return ; for(int i=first[u];i!=-1;i=next[i]) { int to=v[i]; if(to==fa)continue; calc_vis(to,u); if(!vis[to])flag=0; } if(!isLeaf[u])vis[u]=flag; } void clear() { memset(vis,0,sizeof(vis)); memset(sons,0,sizeof(sons)); memset(R,0,sizeof(R)); memset(f,0,sizeof(f)); memset(use,0,sizeof(use)); } int check(long long k) { clear(); int o=1,p=1; for(int i=1;i<=m;i++) { if(dis[Militar[i]]>=k) push_up(Militar[i],k); else R[o].x=k-dis[Militar[i]],R[o++].y=Militar[i]; } calc_vis(1,-1); for(int i=first[1];i!=-1;i=next[i]) { int to=v[i]; if(!vis[to])sons[p].x=w[to],sons[p++].y=to,f[to]=p-1; } std::sort(R+1,R+o,cmp); std::sort(sons+1,sons+p,cmp); int a=1,b=1; for(int i=1;i<o;i++) if(vis[bel[R[i].y]]==0) if(R[i].x<dis[bel[R[i].y]]) vis[bel[R[i].y]]=1,use[i]=1; for(int i=1;a<o&&b<p;i++) { if(use[a]){a++;continue;} if(vis[sons[b].y]==0) { if(R[a].x>=sons[b].x)vis[sons[b].y]=1,a++,b++; else a++; } else {b++;continue;} } return dfs(1,-1); } void BFS(int u,int f) { fa[u]=f; if(next[first[u]]==-1)isLeaf[u]=1; for(int i=first[u];i!=-1;i=next[i]) { int to=v[i]; if(to==f)continue; dis[to]=dis[u]+ww[i],w[to]=ww[i]; BFS(to,u); } } int main() { scanf("%d",&n); long long sum=0; int cnt=0; for(int i=0;i<=n;i++)first[i]=-1; for(int i=1;i<n;i++) { int a,b;long long c=0; scanf("%d %d %I64d",&a,&b,&c);sum+=c; add(a,b,c); add(b,a,c); } scanf("%d",&m); for(int i=1;i<=m;i++) { int a; scanf("%d",&a); Militar[i]=a; hasf[a]=1; } BFS(1,-1); long long l=0,r=sum; for(int i=first[1];i!=-1;i=next[i]) calc(v[i],1,v[i]),cnt++; if(m<cnt){printf("-1");return 0;} if(!check(0)){printf("0\n");return 0;} while(l+1!=r) { long long mid=(long long)(l+r)>>1; if(!check(mid)) r=mid; else l=mid; } printf("%I64d\n",r); }