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。
样例输入:
4
1 2 1
1 3 2
3 4 3
2
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 }