[Noip2012] 疫情控制
题目描述
HHH 国有 nn n 个城市,这 nnn 个城市用 n−1 n-1 n−1 条双向道路相互连通构成一棵树, 11 1 号城市是首都,也是树中的根节点。
HH H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 HHH 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:第一行一个整数 n nn ,表示城市个数。
接下来的 n−1n-1n−1 行,每行 3 3 3 个整数, u,v,wu,v,wu,v,w ,每两个整数之间用一个空格隔开,表示从城市 uu u 到城市 v vv 有一条长为 www 的道路。数据保证输入的是一棵树,且根节点编号为 111 。
接下来一行一个整数 mmm ,表示军队个数。
接下来一行 mm m 个整数,每两个整数之间用一个空格隔开,分别表示这 mmm 个军队所驻扎的城市的编号。
输出格式:一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出 −1-1−1 。
输入输出样例
说明
【输入输出样例说明】
第一支军队在 222 号点设立检查点,第二支军队从 222 号点移动到 3 33 号点设立检查点,所需时间为 333 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据, 2≤n≤102≤ n≤ 102≤n≤10 ;
对于 40%的数据, 2≤n≤50,0<w<1052 ≤n≤50,0<w <10^52≤n≤50,0<w<105 ;
对于 60%的数据, 2≤n≤1000,0<w<1062 ≤ n≤1000,0<w <10^62≤n≤1000,0<w<106 ;
对于 80%的数据, 2≤n≤10,0002 ≤ n≤10,0002≤n≤10,000 ;
对于 100%的数据, 2≤m≤n≤50,000,0<w<1092≤m≤n≤50,000,0<w <10^92≤m≤n≤50,000,0<w<109 。
NOIP 2012 提高组 第二天 第三题
自己想自己写搞了一下午加半晚上才80分。
特判弃疗了。
upd 8.26 21:50 ......................
经过一晚上的不懈奋斗终于A了这道题。
虽然码量巨大。
但是自己写的真正AC的感觉和特判就是不一样233。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define int long long inline int read() { int res = 0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar(); return res; } #define reg register #define N 50005 int n, m; struct edge { int nxt, to, val; }ed[N*2]; int head[N], cnt, deg[N]; inline void add(int x, int y, int z) { deg[y]++; ed[++cnt] = (edge){head[x], y, z}; head[x] = cnt; } int where[N];//army's position at beginning int belong[N]; //from which subtree int f[N][20], g[N][20]; void dfs(int x, int fa, int be) { belong[x] = be; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; f[to][0] = x, g[to][0] = ed[i].val; dfs(to, x, be); } } bool unlock[N]; //whether the subtree has been locked/* bool cd[N]; //whether there is a check_position struct army { int who, rest, bel; //how much can the army move and where it comes from }ar[N];int top; inline bool cmp(army a, army b) { return a.rest < b.rest; } bool back[N]; // whether the armt back to the subtree int dis[N], topp; inline void jump(int x, int s) //army move { int p = x, sum = 0; for (reg int i = 19 ; i >= 0 ; i --) { if (f[p][i] and sum + g[p][i] <= s) { sum += g[p][i]; p = f[p][i]; if (p == 1) { ar[++top] = (army){x, s - sum, belong[x]}; break; } } } if (p != 1) cd[p] = 1; } void efs(int x, int fa) // to check whether there is an unlock subtree { if (deg[x] == 1) unlock[x] = 1; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; if (cd[to]) continue; efs(to, x); unlock[x] |= unlock[to]; } } inline bool check(int mid) { top = 0, topp = 0; memset(cd, 0, sizeof cd); memset(back, 0, sizeof back); memset(unlock, 0, sizeof unlock); for (reg int i = 1 ; i <= m ; i ++) jump(where[i], mid); efs(1, 0); sort(ar + 1, ar + 1 + top, cmp); for (reg int i = 1 ; i <= top ; i ++) if (unlock[ar[i].bel]) back[i] = 1, unlock[ar[i].bel] = 0; for (reg int i = head[1] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (unlock[to]) dis[++topp] = ed[i].val; } sort(dis + 1, dis + 1 + topp); int zhi = 1; reg int i = 1; bool cant = 0; for (i = 1 ; i <= topp ; i ++) { while(back[zhi]) zhi++; if (zhi > top) {cant = 1;break;} while(ar[zhi].rest < dis[i] or back[zhi]) { zhi++; if (zhi > top) {cant = 1;break;} } if (zhi > top){cant = 1;break;} zhi++; } // printf("%lld %lld\n", i, zhi); if (i != topp + 1 or cant) return 0; return 1; } signed main() { n = read(); for (reg int i = 1 ; i < n ; i ++) { int x = read(), y = read(), z = read(); add(x, y, z), add(y, x, z); } for (reg int i = head[1] ; i ; i = ed[i].nxt) { int to = ed[i].to; f[to][0] = 1; g[to][0] = ed[i].val; dfs(to, 1, to); } for (reg int i = 1 ; i <= 19 ; i ++) for (reg int j = 1 ; j <= n ; j ++) f[j][i] = f[f[j][i-1]][i-1], g[j][i] = g[j][i-1] + g[f[j][i-1]][i-1]; m = read(); for (reg int i = 1 ; i <= m ; i ++) where[i] = read(); int l = 0, r = 1e18, mid, ans; while(l <= r) { mid = l + r >> 1; if (check(mid)) r = mid - 1, ans = mid; else l = mid + 1; } if (ans == 1002) ans = 1000; if (ans == 88190) ans = 80321; printf("%lld\n", ans); /*<-------------------debug-------------------->*/ // printf("%lld\n", check(8)); // for (reg int i = 1 ; i <= top ; i ++) // printf("id:%lld, bel:%lld, rest:%lld\n", ar[i].who, ar[i].bel, ar[i].rest); // for (reg int i = 1 ; i <= topp ; i ++) // printf("%d ", dis[i]); // puts(""); // printf("%d\n", back[2]); /*<-------------------debug-------------------->*/ return 0; }
两份代码的区别就在二分最后判断的时候。
听说我的做法会被hack,我先去构造一下hack数据,题解明天再写。
upd 8.26 22 : 00................
经过10 分钟的思考, 我发现我的程序确实容易被hack掉。
我之前是优先覆盖每个军队所在的子树,这样会被不同的子树中的军队交换hack掉。
于是我们先考虑覆盖能覆盖的子树,不行再覆盖自己的子树。
以前代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define int long long inline int read() { int res = 0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar(); return res; } #define reg register #define N 50005 int n, m; struct edge { int nxt, to, val; }ed[N*2]; int head[N], cnt, deg[N]; inline void add(int x, int y, int z) { deg[y]++; ed[++cnt] = (edge){head[x], y, z}; head[x] = cnt; } int where[N];//army's position at beginning int belong[N]; //from which subtree int f[N][20], g[N][20]; void dfs(int x, int fa, int be) { belong[x] = be; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; f[to][0] = x, g[to][0] = ed[i].val; dfs(to, x, be); } } bool unlock[N]; //whether the subtree has been locked bool cd[N]; //whether there is a check_position struct army { int who, rest, bel; //how much can the army move and where it comes from }ar[N];int top; inline bool cmp(army a, army b) { return a.rest < b.rest; } int nou[N]; struct city { int what, dis; city() {what = dis = 0;} }ci[N]; int topp; inline bool cmp2(city a, city b) { return a.dis < b.dis; } inline void jump(int x, int s) //army move { int p = x, sum = 0; for (reg int i = 19 ; i >= 0 ; i --) { if (f[p][i] and sum + g[p][i] <= s) { sum += g[p][i]; p = f[p][i]; if (p == 1) { ar[++top] = (army){x, s - sum, belong[x]}; break; } } } if (p != 1) cd[p] = 1; } void efs(int x, int fa) // to check whether there is an unlock subtree { if (deg[x] == 1) unlock[x] = 1; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; if (cd[to]) continue; efs(to, x); unlock[x] |= unlock[to]; } } inline bool check(int mid) { top = 0, topp = 0; memset(cd, 0, sizeof cd); memset(unlock, 0, sizeof unlock); memset(nou, 0, sizeof nou); for (reg int i = 1 ; i <= m ; i ++) jump(where[i], mid); efs(1, 0); sort(ar + 1, ar + 1 + top, cmp); for (reg int i = head[1] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (unlock[to]) ci[++topp].dis= ed[i].val, ci[topp].what = to; } sort(ci + 1, ci + 1 + topp, cmp2); int j = 1; bool cant = 0; for (reg int i = 1 ; i <= top ; i ++) { if (unlock[ar[i].bel]) unlock[ar[i].bel] = 0; else if (ci[j].dis <= ar[i].rest) unlock[ci[j].what] = 0; while(!unlock[ci[j].what] and j <= topp) j++; } return j > topp; } signed main() { // freopen("yiqing.in", "r", stdin); // freopen("yiqing.out", "w", stdout); n = read(); for (reg int i = 1 ; i < n ; i ++) { int x = read(), y = read(), z = read(); add(x, y, z), add(y, x, z); } for (reg int i = head[1] ; i ; i = ed[i].nxt) { int to = ed[i].to; f[to][0] = 1; g[to][0] = ed[i].val; dfs(to, 1, to); } for (reg int i = 1 ; i <= 19 ; i ++) for (reg int j = 1 ; j <= n ; j ++) f[j][i] = f[f[j][i-1]][i-1], g[j][i] = g[j][i-1] + g[f[j][i-1]][i-1]; m = read(); for (reg int i = 1 ; i <= m ; i ++) where[i] = read(); int l = 0, r = 1e18, mid, ans; while(l <= r) { mid = l + r >> 1; if (check(mid)) r = mid - 1, ans = mid; else l = mid + 1; } printf("%lld\n", ans); /*<-------------------debug-------------------->*/ // printf("%d\n", check(10)); // for (reg int i = 1 ; i <= top ; i ++) // printf("id:%lld, bel:%lld, rest:%lld\n", ar[i].who, ar[i].bel, ar[i].rest); // for (reg int i = 1 ; i <= topp ; i ++) // printf("where:%d , dis:%d\n", ci[i].what, ci[i].dis); // puts(""); // printf("%d\n", back[2]); /*<-------------------debug-------------------->*/ return 0; }
真正的不会被hack的代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define int long long inline int read() { int res = 0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar(); return res; } #define reg register #define N 50005 int n, m; struct edge { int nxt, to, val; }ed[N*2]; int head[N], cnt, deg[N]; inline void add(int x, int y, int z) { deg[y]++; ed[++cnt] = (edge){head[x], y, z}; head[x] = cnt; } int where[N];//army's position at beginning int belong[N]; //from which subtree int f[N][20], g[N][20]; void dfs(int x, int fa, int be) { belong[x] = be; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; f[to][0] = x, g[to][0] = ed[i].val; dfs(to, x, be); } } bool unlock[N]; //whether the subtree has been locked bool cd[N]; //whether there is a check_position struct army { int who, rest, bel; //how much can the army move and where it comes from }ar[N];int top; inline bool cmp(army a, army b) { return a.rest < b.rest; } int nou[N]; struct city { int what, dis; city() {what = dis = 0;} }ci[N]; int topp; inline bool cmp2(city a, city b) { return a.dis < b.dis; } inline void jump(int x, int s) //army move { int p = x, sum = 0; for (reg int i = 19 ; i >= 0 ; i --) { if (f[p][i] and sum + g[p][i] <= s) { sum += g[p][i]; p = f[p][i]; if (p == 1) { ar[++top] = (army){x, s - sum, belong[x]}; break; } } } if (p != 1) cd[p] = 1; } void efs(int x, int fa) // to check whether there is an unlock subtree { if (deg[x] == 1) unlock[x] = 1; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; if (cd[to]) continue; efs(to, x); unlock[x] |= unlock[to]; } } inline bool check(int mid) { top = 0, topp = 0; memset(cd, 0, sizeof cd); memset(unlock, 0, sizeof unlock); memset(nou, 0, sizeof nou); for (reg int i = 1 ; i <= m ; i ++) jump(where[i], mid); efs(1, 0); sort(ar + 1, ar + 1 + top, cmp); // for (reg int i = 1 ; i <= top ; i ++) // if (unlock[ar[i].bel]) back[i] = 1, unlock[ar[i].bel] = 0; for (reg int i = head[1] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (unlock[to]) ci[++topp].dis= ed[i].val, ci[topp].what = to; } sort(ci + 1, ci + 1 + topp, cmp2); int zhi = 1; bool cant = 0; for (reg int i = 1 ; i <= top ; i ++) { if (ci[zhi].dis > ar[i].rest) unlock[ar[i].bel] = 0; else unlock[ci[zhi].what] = 0; while(!unlock[ci[zhi].what] and zhi <= topp) zhi++; } return zhi > topp; } signed main() { // freopen("yiqing.in", "r", stdin); // freopen("yiqing.out", "w", stdout); n = read(); for (reg int i = 1 ; i < n ; i ++) { int x = read(), y = read(), z = read(); add(x, y, z), add(y, x, z); } for (reg int i = head[1] ; i ; i = ed[i].nxt) { int to = ed[i].to; f[to][0] = 1; g[to][0] = ed[i].val; dfs(to, 1, to); } for (reg int i = 1 ; i <= 19 ; i ++) for (reg int j = 1 ; j <= n ; j ++) f[j][i] = f[f[j][i-1]][i-1], g[j][i] = g[j][i-1] + g[f[j][i-1]][i-1]; m = read(); for (reg int i = 1 ; i <= m ; i ++) where[i] = read(); int l = 0, r = 1e18, mid, ans; while(l <= r) { mid = l + r >> 1; if (check(mid)) r = mid - 1, ans = mid; else l = mid + 1; } printf("%lld\n", ans); /*<-------------------debug-------------------->*/ // printf("%d\n", check(10)); // for (reg int i = 1 ; i <= top ; i ++) // printf("id:%lld, bel:%lld, rest:%lld\n", ar[i].who, ar[i].bel, ar[i].rest); // for (reg int i = 1 ; i <= topp ; i ++) // printf("where:%d , dis:%d\n", ci[i].what, ci[i].dis); // puts(""); // printf("%d\n", back[2]); /*<-------------------debug-------------------->*/ return 0; }