NOIP 2012 疫情控制

疫情控制2012年NOIP全国联赛提高组

题目描述:

H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,

也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境

城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境

城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,

首都是不能建立检查点的。

现在,在 H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军

队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在

一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等

于道路的长度(单位:小时)。

请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

 

输入描述:

第一行一个整数 n,表示城市个数。

接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从

城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。

接下来一行一个整数 m,表示军队个数。

接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎

的城市的编号。

 

输出描述:

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

 

样例输入:

1 2 1 

1 3 2 

3 4 3 

2 2 

 

样例输出:

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。

 

首先来分析题意:给定一棵树,用军队来割去一些点。每个军队有初始位置,每条边有边权,

军队移动的时间等于走过的路径长度。求切断所有叶子节点与根节点的连接最短需要多长时间。

 

直接求整个问题的答案感觉无从下手,但是我们会发现一些性质:

一个深度小的点,控制的叶子节点一定不会少于一个深度大的点(同一子树中)。

这样,会发现在时间给定的情况下,根据上面的贪心,每支军队的移动策略其实很容易可以确定

  ————尽可能的走向深度小的节点以控制更多叶子节点。

这样一来,我们可以通过二分答案+检验来解决此题。那么关键就是怎么检验了

想清楚这样一件事:

封锁一个节点的效果,可以转换成,封锁他所有的子节点

由于根节点不能封锁,所以我们将问题转换为,封锁所有根节点的直系儿子

(同理,封锁一个根节点的直系儿子,可以转换成封锁这一儿子的所有儿子)

然后我们按照以上贪心策略来移动军队时,可以把军队分为两类:

1,在规定时间内不能到达根的军队

2,在规定时间内可以到达根的军队

对于第一种军队,最优的位置是一直往上走,最终停下的位置。

对于第二种军队,他又可以有两种选择:

1,到达根节点的直系儿子之后,不再移动,封锁那一节点

2,移动到根节点,并且再往下走,封锁其他节点

先来考虑比较简单的第一种军队,由于第一种军队的最优策略可以直接确定

那我们便按照最优策略进行,然后,如果有一些二层的节点(根的直系儿子)

被这些军队控制(如上面所说,不一定是直接到这个节点控制)了的话,那以后便可以不必考虑这些二层的点

我们要考虑的其实是:如何调度第二类军队,去封锁还没有被控制的二层节点

到这里,我们又可以贪心啦!

能到达根的军队必然会有剩余的时间,我们只要贪心的让剩余时间小的,去控制更近的二层点就好了。

也就是用能控制一个二层的点的剩余时间最少的军队,去控制这个点。

这里还有一个细节:能到达根的军队,如果他来自那个二层节点没有被控制的话,应该让这支军队留在那个二层节点

         不然我们必然要花费另一剩余时间长的军队来控制他,这样不够优。

最后的问题,怎样移动军队,找到军队的位置呢?倍增就可以解决了。

 

下面贴代码

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 
  6 #define RI register int
  7 using namespace std;
  8 typedef long long ll;
  9 
 10 const int INF = 1e9 + 7;
 11 const int MAXN = 50000 + 5;
 12 
 13 #define max(a,b) ((a) > (b) ? (a) : (b))
 14 #define min(a,b) ((a) < (b) ? (a) : (b))
 15 
 16 inline void read(int &x)
 17 {
 18     x = 0;
 19     bool flag = 0;
 20     char ch = getchar();
 21     while(ch < '0' || ch > '9')
 22     {
 23         if(ch == '-')    flag = 1;
 24         ch = getchar();
 25     }
 26     while(ch >= '0' && ch <= '9')
 27     {
 28         x = x * 10 + ch - '0';
 29         ch = getchar();
 30     }
 31     if(flag)    x *= -1;
 32 }
 33 
 34 inline void read(ll &x)
 35 {
 36     x = 0;
 37     bool flag = 0;
 38     char ch = getchar();
 39     while(ch < '0' || ch > '9')
 40     {
 41         if(ch == '-')    flag = 1;
 42         ch = getchar();
 43     }
 44     while(ch >= '0' && ch <= '9')
 45     {
 46         x = x * 10 + ch - '0';
 47         ch = getchar();
 48     }
 49     if(flag)    x *= -1;
 50 }
 51 
 52 struct edge
 53 {
 54     int f,t;
 55     ll v;
 56     edge(int _f,int _t,ll _v)
 57     {f = _f,t = _t,v = _v;}
 58     edge(){}
 59 }l[MAXN << 1];
 60 
 61 ll v,rs;
 62 int n,m,u,t;
 63 int first[MAXN],next[MAXN << 1],tot;
 64 
 65 ll f[MAXN][21];
 66 int g[MAXN][21],army[MAXN];
 67 //f[i][j]表示点i向上跳2^j的父亲
 68 //g[i][j]表示点i跳到向上2^j步的父亲的距离 
 69 
 70 int rtson,rch[MAXN];
 71 //存储根节点的直系儿子 
 72 int cancover[MAXN],son[MAXN],from[MAXN];
 73 //cancover[i]表示覆盖点i是否等价于覆盖i向上到达的根的直系儿子,也就是是否i到二层节点只有一条路径
 74 //son[i]表示i的儿子的数量
 75 //from[i]表示i点向上跳到的二层节点是谁 
 76 
 77 inline void build(int f,int t,ll v)
 78 {
 79     l[++ tot] = edge(f,t,v);
 80     next[tot] = first[f];
 81     first[f] = tot;
 82 }
 83 
 84 void init()
 85 {
 86     tot = 0,rtson = 0,rs = 0;
 87     memset(first,-1,sizeof(first));
 88     read(n);
 89     for(RI i = 1;i < n;i ++)
 90     {
 91         read(u),read(t),read(v);
 92         build(u,t,v),build(t,u,v),rs += v;
 93     }
 94     read(m);
 95     for(RI i = 1;i <= m;i ++)
 96         read(army[i]);
 97 }
 98 
 99 void init_dfs(int u)
100 { 
101     if(g[u][0] == 1)    from[u] = u;
102     else from[u] = from[g[u][0]];
103     for(int i = first[u];i != -1;i = next[i])
104     {
105         int v = l[i].t;
106         if(u == 1)
107             rch[++ rtson] = v;
108         if(v == g[u][0])
109             continue;
110         son[u] ++;
111         g[v][0] = u;
112         f[v][0] = l[i].v;
113         init_dfs(v);
114     }
115 }
116 
117 void dfs(int u)
118 {
119     cancover[u] = 1;
120     if(son[u] > 1)    return;
121     for(int i = first[u];i != -1;i = next[i])
122         if(l[i].t != g[u][0])
123             dfs(l[i].t);
124 }
125 
126 void init_cancover()
127 {
128     for(int i = 1;i <= rtson;i ++)
129         dfs(rch[i]);
130 }
131 
132 void init_rmq_fg()
133 {
134     for(int j = 1;j <= 20;j ++)
135         for(int i = 1;i <= n;i ++)
136         {
137             g[i][j] = g[g[i][j - 1]][j - 1];
138             f[i][j] = f[i][j - 1] + f[g[i][j - 1]][j - 1];
139         }
140 }
141 
142 ll lim;
143 int cnt1,cnt2;
144 //分别表示可调度军队数量,未被控制的城市数量 
145 int pos[MAXN],resdis[MAXN],cover[MAXN];
146 //pos[i]表示军队i最终的位置,resdis[i]表示到达根的军队还能走的距离
147 //cover[i]表示城市i是否被覆盖 
148 
149 void work(int u)
150 {
151     ll tmp = lim;
152     pos[u] = army[u];
153     for(int i = 20;i >= 0;i --)
154     {
155         if(f[pos[u]][i] <= tmp && g[pos[u]][i] != 0)
156         {
157             tmp -= f[pos[u]][i];
158             pos[u] = g[pos[u]][i];
159         }
160     }
161     resdis[u] = tmp;
162 }
163 
164 struct ARMY
165 {
166     int resdis,from;
167     bool operator < (const ARMY &a) const
168     {
169         return resdis < a.resdis;
170     }
171 }A[MAXN];
172 
173 struct CITY
174 {
175     int id,d;
176     bool operator < (const CITY &a) const
177     {
178         return d < a.d;
179     }
180 }B[MAXN];
181 
182 bool check(ll mid)
183 {
184     memset(cover,0,sizeof(cover));
185     lim = mid,cnt1 = cnt2 = 0;
186     for(int i = 1;i <= m;i ++)
187     {
188         work(i);
189         if(pos[i] != 1)
190         {
191             if(cancover[pos[i]])
192                 cover[from[pos[i]]] = 1;
193         }
194         else
195         {
196             cnt1 ++;
197             A[cnt1].resdis = resdis[i];
198             A[cnt1].from = from[army[i]]; 
199         }
200     }
201     for(int i = 1;i <= rtson;i ++)
202     {
203         if(!cover[rch[i]])
204         {
205             cnt2 ++;
206             B[cnt2].id = rch[i];
207             B[cnt2].d = f[rch[i]][0];
208         }
209     }
210     if(cnt2 > cnt1)    return 0;
211     int i = 1,j = 1;
212     sort(A + 1,A + cnt1 + 1);
213     sort(B + 1,B + cnt2 + 1);
214     while(i <= cnt1)
215     {
216         if(!cover[A[i].from])
217             cover[A[i].from] = 1;
218         else
219         {
220             while(j <= cnt2 && cover[B[j].id])    j ++;
221             if(B[j].d <= A[i].resdis) cover[B[j].id] = 1,j ++;
222         }
223         i ++;
224     }
225     for(int i = 1;i <= cnt2;i ++)
226         if(!cover[B[i].id])    return 0;
227     return 1;
228 }
229 
230 ll solve()
231 {
232     ll l = 0,r = rs,mid;
233     while(r - l > 1)
234     {
235         mid = (l + r) >> 1;
236         if(check(mid))
237             r = mid;
238         else
239             l = mid;
240     }
241     if(check(l))    return l;
242     return r;
243 }
244 
245 int main()
246 {
247     init();
248     init_dfs(1);
249     if(rtson > m)
250     {puts("-1");return 0;}
251     init_cancover();
252     init_rmq_fg();
253     printf("%lld\n",solve());
254     return 0;
255 }
View Code

 

posted @ 2017-10-21 20:05  illyaillyasviel  阅读(508)  评论(5编辑  收藏  举报