题目描述
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
说明
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需时间为 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。
NOIP 2012 提高组 第二天 第三题
题解
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long int using namespace std; const int maxn = 50005,maxm = 100005,INF = 2000000000; inline int read(){ int out = 0,flag = 1;char c = getchar(); while (c < 48 || c > 57) {if (c == '-') flag = -1;c = getchar();} while (c >= 48 &&c <= 57) {out = out * 10 + c - 48;c = getchar();} return out * flag; } int N,M,army[maxn],f[maxn][20],cost[maxn][20]; int head[maxn],nedge = 0; struct EDGE{ int to,w,next; }edge[maxm]; inline void build(int u,int v,int w){ edge[nedge] = (EDGE) {v,w,head[u]}; head[u] = nedge++; edge[nedge] = (EDGE) {u,w,head[v]}; head[v] = nedge++; } void init(){ memset(head,-1,sizeof(head)); N = read(); int a,b,w; for (int i = 1; i < N; i++){ a = read(); b = read(); w = read(); build(a,b,w); } M = read(); for (int i = 1; i <= M; i++) army[i] = read(); } int outde[maxn]; void dfs1(int u,int fa){ f[u][0] = fa; for (int k = head[u]; k != -1; k = edge[k].next) if (edge[k].to != fa){ outde[u]++; cost[edge[k].to][0] = edge[k].w; dfs1(edge[k].to,u); } } bool vis[maxn]; bool dfs(int u,int fa){ if (vis[u]) return true; if (!outde[u]) return false; for (int k = head[u]; k != -1; k = edge[k].next) if (edge[k].to != fa && !dfs(edge[k].to,u)) return false; return true; } struct node{ int id,w; }A[maxn],B[maxn]; int ai = 0,bi; int restmin[maxn],resti[maxn],used[maxn]; inline bool operator < (const node& a,const node& b){ return a.w > b.w; } inline bool check(int lim){ int T = max(N,M); for (int i = 1; i <= T; i++){ vis[i] = resti[i] = used[i] = 0; } used[0] = true; int u,w = 0,p; ai = bi = 0; for (int i = 1; i <= M; i++){ u = army[i]; w = 0; while (1){ p = -1; while (f[u][p + 1] > 1 && cost[u][p + 1] + w <= lim) p++; if (p < 0) break; w += cost[u][p]; u = f[u][p]; } if (f[u][0] != 1 || cost[u][0] + w >= lim) vis[u] = true; else { B[++bi] = (node){i,lim - w - cost[u][0]}; if (!resti[u] || restmin[u] > B[bi].w) restmin[u] = B[bi].w,resti[u] = i; } } for (int k = head[1]; k != -1; k = edge[k].next) if (!dfs(edge[k].to,1)) A[++ai] = (node) {edge[k].to,edge[k].w}; sort(A + 1,A + 1 + ai); sort(B + 1,B + 1 + bi); int j = 1; for (int i = 1; i <= ai; i++){ if (!used[resti[A[i].id]]) {used[resti[A[i].id]] = true;continue;} while (j <= bi && (used[B[j].id] || B[j].w < A[i].w)) j++; if (j > bi) return false; used[B[j++].id] = true; } return true; } void solve(){ if (M < outde[1]) {printf("-1 "); return;} for (int k = 1; k < 20; k++) for (int i = 1; i <= N; i++){ f[i][k] = f[f[i][k - 1]][k - 1]; cost[i][k] = cost[i][k - 1] + cost[f[i][k - 1]][k - 1]; } int L = 0,R = 1000000ll; while (L < R){ int mid = (L + R) >> 1; if (check(mid)) R = mid; else L = mid + 1; } printf("%d ",R); } int main(){ init(); dfs1(1,0); solve(); return 0; }