2013之前TG质量还是挺高的w
题目解释不清楚w你是懒吧啊喂! CodeVS传送门
考虑到每个军队一定是往根的方向走(能控制更多的城市),那么只需计算往上走的方式。
这么屌的题目一看就是二分答案(虽然我不知道怎么看出来的=w=)
转为判定性问题:时间为w,能否守住所有路。
LCA计算所有军队都往上走到的最远处(或是根),记录驻扎处(或是到根的军队以后剩下的可移动长度,按剩余长度排序*)。
dfs处理没能守住的、与顶点直接相邻的边,并按边长排序。
这时在根的军队有两个决策①挑一个边过去守②(无代价)退回上来的边。
这时*处排序就有用了:对于可移动长度最短的边,一定让它退回原边(因为它是最没用的),除非原边已守。将不能退的军队从小到大和剩余的没有守住的边用恰当方式守住(仿归并O(n)),发现守不住即可退出。
上面三行有一坨剪枝(如可移动军队与未守边的数量关系等),能AC就不赘述了。
近4K毒瘤丑Code:(三个DFS我也不知道我在想啥)
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define maxn 50010 #define maxm 100010 using namespace std; struct Edge { int from,to,w; }; Edge es[maxm]; int first[maxn],next[maxm],n,m,a[maxn],b[maxn],Si=0,haha=0; int v[maxn],vv[maxn]; void AddEdge(int from,int to,int w) { Si++; es[Si]=(Edge){from,to,w}; next[Si]=first[from]; first[from]=Si; } int p[maxn][50];//以i跳2^j格的点 long long w[maxn][50];//跳的范围内 int S[maxn]; long long l=0,r=0,u; int h[maxn];//每点深度= = long long t1[maxn]; int dfs(int u) { for (int i=first[u];i!=-1;i=next[i]) if (!h[es[i].to]) { Edge e=es[i]; h[e.to]=h[u]+1; p[e.to][0]=u; w[e.to][0]=e.w; dfs(e.to); } } void init() { for (int j=1;(1<<j)<=n;j++) for (int i=1;i<=n;i++) if (p[i][j-1]!=-1) { p[i][j]=p[p[i][j-1]][j-1]; w[i][j]=w[i][j-1]+w[p[i][j-1]][j-1]; } } long long LCA(int a,int b) { int i,ans=0; if (h[a]<h[b]) swap(a,b); for (i=0;(1<<i)<=h[a];i++); i--; for (int j=i;j>=0;j--) if (h[a]-(1<<j)>=h[b]) { ans+=w[a][j]; a=p[a][j]; } if (a==b) return ans; for (int j=i;j>=0;j--) { if (p[a][j]!=-1&&p[a][j]!=p[b][j]) { ans+=w[a][j]; a=p[a][j]; ans+=w[b][j]; b=p[b][j]; } } return ans+w[a][0]+w[b][0]; } int Swim(int a,long long ww) { int i; for (i=0;(1<<i)<=h[a];i++); i--; for (int j=i;j>=0;j--) { if (ww-w[a][j]>=0) { ww-=w[a][j]; a=p[a][j]; } } return a; } int Dfs(int u,int f) { S[u]=f; for (int i=first[u];i!=-1;i=next[i]) if (!S[es[i].to]) Dfs(es[i].to,f); } void read() { scanf("%d",&n); for (int i=1;i<=n;i++) first[i]=-1; for (int i=1;i<=n-1;i++) { long long u,v,w; scanf("%lld%lld%lld",&u,&v,&w); r+=w; AddEdge(u,v,w); AddEdge(v,u,w); } scanf("%d",&m); for (int i=1;i<=m;i++) scanf("%d",&a[i]); h[1]=1; dfs(1); init(); S[1]=-1; for (int i=first[1];i!=-1;haha++,i=next[i]) { Dfs(es[i].to,es[i].to); } } int DFS(int u) { if (b[u]) return 1; v[u]=1; int w=1,x=0; for (int i=first[u];i!=-1;i=next[i]) if (!v[es[i].to]) { w&=DFS(es[i].to); if (!w) return 0; x=1; } if (x==0) return 0; return w; } int left1[maxn],left2[maxn]; int cmp1(int a,int b) { return t1[a]>t1[b]; } int cmp2(int a,int b) { return es[a].w<es[b].w; } int check(long long w) { memset(b,0,sizeof(b)); memset(v,0,sizeof(v)); memset(vv,0,sizeof(vv)); int Siz1=0,Siz2=0; for (int i=1;i<=m;i++) { if (t1[a[i]]>w) b[Swim(a[i],w)]=1; else left1[++Siz1]=a[i];//w-t1[i] } v[1]=1; for (int i=first[1];i!=-1;i=next[i]) if (!DFS(es[i].to)) left2[++Siz2]=i;//es[i].w else vv[es[i].to]=1; if (Siz1<Siz2) return 0; sort(left1+1,left1+1+Siz1,cmp1); sort(left2+1,left2+1+Siz2,cmp2); int j=1; for (int i=1;i<=Siz1;i++) { if (!vv[S[left1[i]]]) vv[S[left1[i]]]=1; else { while (j<=Siz2&&vv[es[left2[j]].to]) j++; while (i<=Siz1&&w-t1[left1[i]]<es[left2[j]].w) i++; if (j>Siz2) return 1; if (i>Siz1) return 0; vv[es[left2[j]].to]=1; } } while (j<=Siz2&&vv[es[left2[j]].to]) j++; if (j<=Siz2) return 0; return 1; } int main() { freopen("BL.in","r",stdin); read(); if (haha>m) { printf("-1"); return 0; } l=0; for (int i=1;i<=m;i++) t1[a[i]]=LCA(a[i],1); while (l<r) { u=l+r>>1; if (check(u)) r=u; else l=u+1; } long long ans; if (r==u) ans=u; if (l==u+1) ans=u+1; printf("%lld",ans); return 0; }