noip2012 疫情控制

描述

H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。 
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。 
现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。 
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

格式

输入格式

第一行一个整数n,表示城市个数。 
接下来的n-1行,每行3个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市u到城市v有一条长为w的道路。数据保证输入的是一棵树,且根节点编号为1。 
接下来一行一个整数m,表示军队个数。 
接下来一行m个整数,每两个整数之间用一个空格隔开,分别表示这m个军队所驻扎的城市的编号。

输出格式

共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

数据范围

保证军队不会驻扎在首都。 
对于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。

----------------------------------------------------------------------

应该算是一道厉害的二分+贪心吧 ;

如果军队数小于根结点的儿子数显然无解- =,不让必然有解。

很容易想到军队玩往树上爬,爬到顶了,可以爬到没被控制的1的叶子节点,直到把所有点控制。

然后想到二分时间让它们爬。

然后我就不会了。。想不到科学的检验能否控制方法0_0,然后找G-word讲解了。。。

如何检测能否控制 :

  在二分的限制时间 T 里,把军队分成两类 :

     A 不能到达1的,也就是没机会爬到1的其他叶子节点 ;

     B 能到达1的,有机会爬到1的其他叶子节点;

  对于通过A类可以求出当前A类未能控制的节点,及其由根结点1到其的路程;

  通过B类可求出B类所有点到1所剩的时间。

  显然所剩时间多的,要去控制路程长的- =,可以排个序(贪心),搞个裸匹配。

  但这样会出现自己跑会自己原来节点的情况- = 

  搞个细节处理之 : 

              从大到小排序(从到大小进行匹配)

              如果当前的军队要控制的节点是后面某个节点走出来的节点 ,

              显然,当前军队所剩时间最多,他要控制的路径如果可以用后面节点控制,显然会更省时,也更科学;

              所以当要控制节点p,如果从它走出来的军队还未使用,就用其中所剩时间最小的控制之- = 

              搞几个数组搞之即可。。。 

  Ps.这题还是开long long吧。。

代码如下:

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<string>
  5 #include<cmath>
  6 #include<queue>
  7 #define INF 999999999999
  8 #define LL long long
  9 #define Max(x,y) if(x<y) x=y;
 10 #define N 100003
 11 using namespace std ;
 12 struct P{
 13     LL p,t;
 14     P():p(),t(){}
 15     P(const LL x,const LL y) : p(x),t(y){}   
 16 }army[N],edge[N];
 17 bool cmp(const P &x,const P &y){
 18     return x.t>y.t; 
 19 } 
 20 LL n,m,T;
 21 LL next[N*2],last[N*2],to[N*2];
 22 LL w[N*2];
 23 LL p[N],from[N],root[N];
 24 LL dis[N],left[N]; 
 25 bool use[N]; 
 26 queue<LL> q;
 27 inline void addedge(LL a,LL b,LL c){
 28     next[++T]=last[a]; last[a]=T; to[T]=b ; w[T]=c;
 29     next[++T]=last[b]; last[b]=T; to[T]=a ; w[T]=c;
 30 }
 31 void fail(){
 32     LL Tot=0;
 33     for(LL i=last[1];i;i=next[i]) ++Tot;
 34     if(Tot>m) {
 35         printf("-1");
 36         exit(0);
 37     }
 38 }
 39 LL pushup(LL now,LL f){
 40      if(!next[last[now]]) {
 41         if(left[now]>=0ll) use[now]=1 ;
 42         return left[now]   ;
 43      }
 44      use[now]=1;
 45      for(LL i=last[now];i;i=next[i]) 
 46       if(to[i]!=f){
 47         Max(left[now],pushup(to[i],now)-w[i]);
 48         if(!use[to[i]]) use[now]=0;
 49       }
 50      if(left[now]>=0) use[now]=1;
 51      return left[now];
 52 }
 53 bool check(LL lim){
 54     LL numa=0,numb=0;
 55     memset(left,-1,sizeof left);
 56     memset(use,0,sizeof use);
 57     memset(from,0,sizeof from);
 58     for(LL i=1;i<=m;i++) 
 59       if(dis[p[i]]>=lim) 
 60         left[p[i]]=lim;
 61       else 
 62         army[++numa]=P(root[p[i]],lim-dis[p[i]]);
 63     pushup(1,0);    
 64     for(LL i=last[1];i;i=next[i]) 
 65       if(!use[to[i]])
 66         edge[++numb]=P(to[i],w[i]);
 67     if(numb>numa) return false; // 军队不够 
 68     sort(army+1,army+numa+1,cmp);
 69     sort(edge+1,edge+numb+1,cmp);
 70     //----来自该节点的所剩时间最少的军队----  
 71     for(LL i=numa;i;i--)
 72        if(!from[army[i].p]) 
 73          from[army[i].p]=i;
 74     //-------------------------------------- 
 75     LL la=1,lb=1;
 76     memset(use,0,sizeof use); 
 77     use[0]=1;
 78     while(lb<=numb&&la<=numa){
 79        if(use[la]){
 80           ++la;
 81           continue ;    
 82        }
 83        if(!use[from[edge[lb].p]]){
 84           use[from[edge[lb].p]]=1;
 85           ++lb;
 86           continue ;
 87        }
 88        if(army[la].t<edge[lb].t) return false ;
 89        use[la]=1;
 90        ++la ;
 91        ++lb ; 
 92     }
 93     return true ; 
 94 }
 95 void prework(){
 96    q.push(1);
 97    use[1]=1;
 98    for(LL i=last[1];i;i=next[i]) root[to[i]]=to[i];
 99    while(!q.empty()){
100         LL now=q.front();
101         q.pop();
102         for(LL i=last[now];i;i=next[i]) 
103         if(!use[to[i]]){
104             if(now!=1) root[to[i]]=root[now];
105             dis[to[i]]=dis[now]+w[i];
106             use[to[i]]=1;
107             q.push(to[i]);
108         }
109    }
110 }
111 int main(){
112     //freopen("xx.in","r",stdin);
113     //freopen("xx.out","w",stdout);
114     scanf("%I64d",&n);
115     LL a,b,c;
116     for(LL i=1;i<n;i++){
117         scanf("%I64d%I64d%I64d",&a,&b,&c);
118         addedge(a,b,c);
119     }
120     scanf("%I64d",&m);
121     fail();
122     for(LL i=1;i<=m;i++) scanf("%I64d",&p[i]);
123     LL ans,l=0,r=1000000000000LL;
124     prework();
125     while(l<=r){
126         LL mid=(l+r)>>1;
127         if(check(mid)){
128             ans=mid;
129             r=mid-1;
130         } else l=mid+1;
131     }
132     printf("%I64d",ans);    
133 }
View Code