题目描述
H 国有 n个城市,这 n 个城市用n−1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:
第一行一个整数n,表示城市个数。
接下来的 n−1 行,每行3个整数,u,v,w,每两个整数之间用一个空格隔开,表示从城市 u到城市v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
输出格式:
一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出−1。
输入输出样例
说明
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到3 号点设立检查点,所需时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤n≤10;
对于 40%的数据,2≤n≤50,0<w<105;
对于 60%的数据,2≤n≤1000,0<w<106;
对于 80%的数据,2≤n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w<109。
NOIP 2012 提高组 第二天 第三题
Solution
感天动地!!终于调出来了!!想是好想,代码实现细节好多aaaaa!!!!QAQ
二分答案,check中间将军队倍增贪心向上跳,如果可以跳到根节点,那么剩下还可以跳的值意味着它可以转移到根节点的其它儿子去,如果不能跳到根节点意味着它必须固定在能跳到的最高位置,打标记。
统计标记,如果子树全都有标记那么它也有标记。
统计哪些根节点的儿子没有被标记,这些节点就有可能从之前统计的可以转移出来的这些点移过来。
所以排序后双指针扫描,如果在统计标记后原来的位置都还没有被打标记,那么这个位置就不能移出去了,不然如果满足移出去的路径长度,那么就给移出去的位置打标记。
错的好惨:
for(int p = 20; p >= 0; p --) if(l + len[u][p] <= mid && jum[u][p] != 0) l += len[u][p], u = jum[u][p];
这里应该先更新长度再跳,之前一直先跳再更新QAQ
#include<bits/stdc++.h> #define LL long long using namespace std; int read() { int x = 0, t = 0; char ch = getchar(); while(!isdigit(ch)) t |= (ch == '-'), ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x *= (t ? -1 : 1); } struct Node { int v, nex, w; } Edge[500005]; int h[50005], stot; void add(int u, int v, int w) { Edge[++stot] = (Node) {v, h[u], w}; h[u] = stot; } int n, m; int jum[50005][21]; LL len[50005][21], dis[50005]; void dfs(int u, int f) { jum[u][0] = f; for(int p = 1; p <= 20; p ++) jum[u][p] = jum[jum[u][p - 1]][p - 1], len[u][p] = len[u][p - 1] + len[jum[u][p - 1]][p - 1]; for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == f) continue; len[v][0] = Edge[i].w; dis[v] = dis[u] + Edge[i].w; dfs(v, u); } } int tag[50005]; void get_tag(int u, int f) { int d = 0; int p = 1; for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == f) continue; get_tag(v, u); d ++; p &= tag[v]; } if(p && d && u != 1) tag[u] = 1; } int pos[50005]; struct Point { int id; LL w; } p[50005], q[50005]; bool cmp(Point a, Point b) { return a.w < b.w; } bool check(int mid) { int sd = 0, sb = 0; memset(tag, 0, sizeof(tag)); for(int i = 1; i <= m; i ++) { int u = pos[i], l = 0; for(int p = 20; p >= 0; p --) if(l + len[u][p] <= mid && jum[u][p] != 0) l += len[u][p], u = jum[u][p]; if(u != 1) tag[u] = 1; else { int x = pos[i]; q[++sb].w = mid - l; for(int p = 20; p >= 0; p --) if(jum[x][p] > 1) x = jum[x][p]; q[sb].id = x; } } get_tag(1, 0); for(int i = h[1]; i; i = Edge[i].nex) { int v = Edge[i].v; if(tag[v]) continue; p[++sd].w = dis[v]; p[sd].id = v; } sort(q + 1, q + 1 + sb, cmp); sort(p + 1, p + 1 + sd, cmp); int l = 1; for(int i = 1; i <= sb; i ++) { if(!tag[q[i].id]) tag[q[i].id] = 1; else if(q[i].w >= p[l].w) tag[p[l].id] = 1; while(tag[p[l].id] && l <= sd) ++ l; } return l > sd; } LL sum; LL erfen() { LL l = 0, r = sum, ans; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } return ans; } int main() { n = read(); for(int i = 1; i < n; i ++) { int u = read(), v = read(), w = read(); add(u, v, w); add(v, u, w); } m = read(); int tmp = 0; for(int i = h[1]; i; i = Edge[i].nex) tmp ++; if(tmp > m) { puts("-1"); return 0; } dfs(1, 0); for(int i = 1; i <= n; i ++) sum += dis[i]; for(int i = 1; i <= m; i ++) pos[i] = read(); LL ans = erfen(); printf("%lld", ans); return 0; }