NOIP2012 疫情控制
Description
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境
城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境
城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是, 首都是不能建立检查点的。
现在,在 H
国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在
一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等 于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
Input
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从 城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎 的城市的编号。
Output
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
Sample Input
4
1 2 1
1 3 2
3 4 3
2
2 2
Sample Output
3
Hint
样例说明:
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需 时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 10;
对于 40%的数据,2 ≤n≤50,0<w <10^5;
对于 60%的数据,2 ≤ n≤1000,0<w <10^6;
对于 80%的数据,2 ≤ n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w <10^9
noip2012真是可怕的贪心年...
二分答案,然后考虑如何check;
本题有一个比较明显的贪心策略,军队要尽量往上走,这样才能封锁更多的道路;
对于一支军队,在mid时间内有两种情况:
1.能走到根节点,相当于一个游走的救火部队;
2.不能走到根节点,尽量往上走,这样能封锁更多的道路;
对与不能走到根节点的军队,要走到能走到的深度最低的节点,并打上标记;
通过一次从下到上的dfs能够判断用着些不能到达根节点的军队能使深度为2的点不用放军队了;
对于能到达根节点的军队,记下到达根节点后还有多少时间,然后他们的最优策略是去覆盖深度为2的点中没有被覆盖的点(这样控制得最多);
于是问题转化成一个匹配问题,用军队去匹配未覆盖的点,是一个很经典的贪心问题,肯定是用剩余时间多的去救离根节点距离远的点;
所以两遍sort然后用指针扫一下判断即可
本蒟蒻打的是每遍爆搜,懒得打倍增...
#include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> #define RG register using namespace std; typedef long long ll; const int N=100050; const int Inf=2147483647; int gi() { int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int to[N],head[N],nxt[N],cnt,w[N],n,m,pos[N],vis[N],st[N],tot,tot1; int deep[N],flag,hi,mdep; struct data{ int tim,from; }done[N],nodone[N]; bool cmp(const data &a,const data &b){ return a.tim<b.tim; } inline void lnk(RG int x,RG int y,RG int z){ to[++cnt]=y,w[cnt]=z,nxt[cnt]=head[x],head[x]=cnt; to[++cnt]=x,w[cnt]=z,nxt[cnt]=head[y],head[y]=cnt; } inline void dfs2(RG int x,RG int fa){ deep[x]=deep[fa]+1; for(RG int i=head[x];i;i=nxt[i]){ int y=to[i];if(y==fa) continue; dfs2(y,x); } } inline void dfs(RG int x,RG int lim,RG int tim,RG int fa){ if(tim>lim) return; if(deep[x]<mdep) mdep=deep[x],hi=x; if(x==1){ done[++tot].from=fa; done[tot].tim=lim-tim; flag=1;return; } for(RG int i=head[x];i;i=nxt[i]){ int y=to[i]; if(y!=fa) dfs(y,lim,tim+w[i],x); } } inline void dfs3(int x,int fa){ int pd=1,pd2=1; for(int i=head[x];i;i=nxt[i]){ int y=to[i];if(y==fa) continue; pd2=0;dfs3(y,x);if(!vis[y]) pd=0; } if(pd2==0&&pd==1) vis[x]=1; } inline bool check(RG int mid){ tot=0,tot1=0; memset(vis,0,sizeof(vis)); for(RG int i=1;i<=m;i++){ flag=0;hi=st[i];mdep=deep[st[i]]; dfs(st[i],mid,0,st[i]); if(!flag) vis[hi]=1; } dfs3(1,0); for(RG int i=head[1];i;i=nxt[i]){ if(!vis[to[i]]){ nodone[++tot1].from=to[i]; nodone[tot1].tim=w[i]; } } sort(done+1,done+1+tot,cmp); sort(nodone+1,nodone+1+tot1,cmp); if(tot1==0) return 1; int j=1; for(RG int i=1;i<=tot;i++){ if(!vis[done[i].from]) vis[done[i].from]=1; else if(done[i].tim>=nodone[j].tim) vis[nodone[j].from]=1; while(vis[nodone[j].from]&&j<=tot1) j++; if(j==tot1+1) return 1; } return 0; } main(){ n=gi(); for(RG int i=1;i<n;i++){ int x=gi(),y=gi(),z=gi(); lnk(x,y,z); } dfs2(1,0);m=gi();for(int i=1;i<=m;i++) st[i]=gi(); int l=0,r=Inf,ans=Inf; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans==Inf) puts("-1"); else printf("%d\n",ans); }