题目描述
HH 国有 nn个城市,这 n 个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。
HH国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 HH 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:
第一行一个整数nn,表示城市个数。
接下来的 n-1n−1 行,每行33个整数,u,v,w,每两个整数之间用一个空格隔开,表示从城市 uu到城市vv 有一条长为 ww 的道路。数据保证输入的是一棵树,且根节点编号为 11。
接下来一行一个整数 mm,表示军队个数。
接下来一行 mm个整数,每两个整数之间用一个空格隔开,分别表示这 mm 个军队所驻扎的城市的编号。
输出格式:
一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1−1。
输入输出样例
说明
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到3号点设立检查点,所需时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 102≤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<bits/stdc++.h> using namespace std; #define ll long long const int M = 50005, P = 20; bool fg[M], vis[M], ok[M]; int h[M], tot, anc[M][P+1], n, t[M], top[M]; ll dis[M]; struct edge{int v, nxt, w;}G[M<<1]; void add(int u, int v, int w){ G[++tot].v = v, G[tot].nxt = h[u], G[tot].w = w, h[u] = tot; } struct node{int id, w;}stk[M], sp[M], res[M]; bool cmp(node A, node B){return A.w > B.w;} int ch, m; void dfs(int u, int f, int w, int zx){ anc[u][0] = f; top[u] = zx; for(int p = 1; p <= P; p++) anc[u][p] = anc[anc[u][p-1]][p-1]; dis[u] = dis[f] + 1LL*w; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == f)continue; ch += u == 1; //if(u == 1) kp[v] = 1; int nzx = zx ? zx : v; dfs(v, u, G[i].w, nzx); } } int get(int u, int f){ fg[u] = 1; int child = 0; if(ok[u]) return fg[u] = ok[u]; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == f)continue; child++; if(!get(v, u)){fg[u] = 0;} } if(!child) fg[u] = ok[u] != 0; return fg[u]; } bool check(ll k){ memset(ok, 0, sizeof(ok)); memset(vis, 0, sizeof(vis)); memset(fg, 0, sizeof(fg)); for(int i = 1; i <= n; i++)res[i] = (node) {0, 0}; int pf = 0, tp = 0; for(int i = 1; i <= m; i++){ int u = t[i]; if(u == 1){stk[++tp] = (node) {1, k};continue;} if(dis[u] <= k) { ll w = k - dis[u]; stk[++tp] = (node) {i, w}; if(!res[top[u]].id || res[top[u]].w > w){ res[top[u]] = (node) {i, w}; } continue; } ll tmp = k; for(int p = P; p >= 0 && tmp; p--) if(dis[u] - dis[anc[u][p]] <= tmp) tmp -= (dis[u] - dis[anc[u][p]]), u = anc[u][p]; ok[u] = 1; } get(1, 0); for(int i = h[1]; i; i = G[i].nxt){ int v = G[i].v; if(!fg[v])sp[++pf].id = G[i].v, sp[pf].w = G[i].w; } sort(stk + 1, stk + 1 + tp, cmp); if(pf > tp)return 0; sort(sp + 1, sp + 1 + pf, cmp); int i = 1, j = 1; vis[0] = 1; while(j <= pf){ int u = sp[j].id; if(!vis[res[u].id]){vis[res[u].id]=1;j++;continue;} while(i <= tp && (vis[stk[i].id] || stk[i].w < sp[j].w))i++; if(i > tp)return 0; vis[stk[i].id]=1; i++;j++; } return j > pf; } int main(){ // freopen("testdata (2).in","r",stdin); int u, v, w; scanf("%d", &n); for(int i = 1; i < n; i++){ scanf("%d%d%d", &u, &v, &w); add(u, v, w); add(v, u, w); } scanf("%d", &m); for(int i = 1; i <= m; i++)scanf("%d", &t[i]); dfs(1, 0, 0, 0); top[1] = 0; if(ch > m){ puts("-1");return 0; } ll lf = 0, rg = 500000, ans = -1; while(lf <= rg){ ll mid = (lf + rg) >> 1; if(check(mid)) rg = mid - 1, ans = mid; else lf = mid + 1; } printf("%lld ", ans); }