洛谷 P1084 疫情控制 noip2013D2T3
题目描述
H国有n个城市,这 n 个城市用n−1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:
第一行一个整数n,表示城市个数。
接下来的 n−1 行,每行3个整数,u,v,w,每两个整数之间用一个空格隔开,表示从城市 u到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
输出格式:
一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出−1。
输入输出样例
说明
【输入输出样例说明】
第一支军队在 22 号点设立检查点,第二支军队从 22 号点移动到33 号点设立检查点,所需时间为 33 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 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。
NOIP 2012 提高组 第二天 第三题
errrrrrrrrrrrrrrrrrrrra 我要被这道题恶星死了!!!! 全是细节!!!我写了一个小时调了三个多小时.. 我他妈真实爆哭
这道题思路还是比较简单的 首先这道题求最少时间 二分性显然 所以考虑二分
首先可以发现军队越向上走能够控制的点就越多 所以我们可以贪心判断军队是否可以跳到根节点
二分最短时间为$tim$ 将他跳到根节点之后的剩余时间记做$res$ 将军队往上跳时经过的根节点的儿子节点记做$top$
如果这个点不能够跳到根节点 即$res <0$ 那么他只需要尽量往上跳即可 在他最后跳到的点打上$vis$标记 因为它没有跨越根节点支援其他子树的能力 这个显然是用倍增优化
其他具有支援能力的加入一个数组 在做完这些之后将已经打上的标记上传 条件是所有的儿子都被控制了 那么他自己就被控制了
那么怎么判断剩余是否合法呢
将所有没打标记的点加入另一个数组 两数组分别排序用双指针一一比较即可
但是有一种特殊情况 如果这个点能够跳到根节点 但是他的剩余时间都不够使自己返回$top$ 那么他还是呆在$top$好了
对于这种特例还有种特例 就是他的下方军队无法跳到根节点 并且已经控制了$top$这条路径 那么他就不需要呆在$top$了 可以对其他点产生贡献 即使贡献很小
最后如果根节点所有的儿子都可以被打上标记 那么他就是合法的 更多细节写在代码里了
代码
#include <bits/stdc++.h> #define il inline #define rg register #define oo 5e13 using namespace std; typedef long long ll; const int N = 5 * 1e4 + 5; const int P = 25; int tot, head[N], nex[2 * N], tov[2 * N], val[2 * N]; int l[N], ys[N], n, m, a[N], cnt, cn; ll ans, S, dis[N], len[N][P], anc[N][P]; bool vis[N]; struct node { int w, id; bool use; }f[N], h[N]; il bool cmp(const node & a, const node & b) { return a.w < b.w; } il int read( ) { int t = 1, ans = 0; char x; x = getchar( ); while(x < '0' || x > '9') { if(x == '-') t = -1; x = getchar( ); } while(x >= '0' && x <= '9') { ans = ans * 10 + x - '0'; x = getchar( ); } return ans * t; } il void add(int u, int v, int w) { tot ++; nex[tot] = head[u]; tov[tot] = v; head[u] = tot; val[tot] = w; } il void dfs(int u, int fa) { for(rg int p = 1;p <= 15;p ++) len[u][p] = len[u][p - 1] + len[anc[u][p - 1]][p - 1], anc[u][p] = anc[anc[u][p - 1]][p - 1]; for(rg int i = head[u];i;i = nex[i]) { int v = tov[i]; if(v == fa) continue; len[v][0] = val[i], anc[v][0] = u; dis[v] = dis[u] + val[i]; l[v] = u == 1 ? val[i] : l[u];//top到根节点的路径的值 ys[v] = u == 1 ? v : ys[u];//对应每个节点的top dfs(v, u); } } il void Init( ) { n = read( ); for(rg int i = 1;i < n;i ++) { int u, v, w; u = read( ), v = read( ), w = read( ); add(u, v, w); add(v, u, w); S += w; } m = read( ); for(rg int i = 1;i <= m;i ++) a[i] = read( ); for(rg int i = 1;i <= n;i ++) for(rg int j = 1;j <= 15;j ++) len[i][j] = oo; dfs(1, 0); } il void make_tag(int u, int fa) { int r = 0, x = 0; for(rg int i = head[u];i;i = nex[i]) { int v = tov[i]; if(v == fa) continue; make_tag(v, u); r ++; x += vis[v]; } if(r == x && r) vis[u] = true; } il void clear( ) { for(rg int i = 1;i <= n;i ++) vis[i] = false; cnt = 0; cn = 0; } il bool check(ll tim) { for(rg int i = 1;i <= m;i ++) { int top = a[i]; ll res = tim - dis[top]; if(res > 0 && res < l[top]) {f[++ cnt].w = res; f[cnt].id = ys[top]; f[cnt].use = true; continue;}//打他是特例的标记 if(res > 0) {f[++ cnt].w = res; f[cnt].id = ys[top]; f[cnt].use = false; continue;} ll t = tim; for(rg int p = 15;p >= 0;p --) { if(t >= len[top][p] && len[top][p] && anc[top][p] && anc[top][p] != 1) //不能跳到根节点 长度非零 祖先非零 t -= len[top][p], top = anc[top][p];//先减再跳 否则len会变化 } vis[top] = true; } make_tag(1, 0); for(rg int i = head[1];i;i = nex[i]) { int v = tov[i]; if(vis[v]) continue; h[++ cn].w = val[i]; h[cn].id = v; } sort(f + 1, f + cnt + 1, cmp); sort(h + 1, h + cn + 1,cmp); if(! cn) return true; int h1 = 1, h2 = 1; while(h2 <= cn && h1 <= cnt) { if(vis[h[h2].id]) { h2 ++; continue;//如果已经被标记了 就重新开始判断 因为可能有不止一个h2对应的被打了标记 } if(! vis[f[h1].id] && f[h1].use) { vis[f[h1].id] = true, h1 ++; continue;//同理 } if(h[h2].w <= f[h1].w) vis[h[h2].id] = true, h1 ++, h2 ++; else h1 ++; } for(rg int i = head[1];i;i = nex[i]) { int v = tov[i]; if(! vis[v]) return false; } return true; } il void Solve( ) { ll l = 0, r = S, ans = -1; while(l <= r) { ll mid = l + r >> 1; clear( ); if(check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } printf("%lld\n", ans); } int main( ) { Init( ); Solve( ); }