Description:
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
Input:
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
Output:
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
思路:首先二分答案,然后对于每支军队,到根节点的子节点的距离小于mid的全都到根节点的子节点集中,其他军队都尽量往上走,能走到哪就是哪。然后将根节点的子节点上的军队剩余时间排序,剩余时间最小且无法做到去一趟根节点再回来的军队驻守在原来的子节点上,其他军队都到根节点上,然后排序,并将所有没有军队的子节点排序,贪心扫描一遍即可
#include<algorithm> #include<iostream> #include<vector> #include<cstring> #include<cstdio> #define ll long long using namespace std; const int N = 50050; typedef pair<int,int> P; int head[N], now; struct edges{ int to, next, w; }edge[N<<1]; void add(int u,int v,int w){ edge[++now] = {v, head[u], w}; head[u] = now;} //dep存储深度,pre存储每个点父节点,e_pre存储每个点的上一条边 dict存储每个点所对应的根节点的子节点 //pos存储军队初始位置 f存储每个根节点的子节点上的病毒能否到达叶子 int n, m, pre[N], pos[N], dict[N], e_pre[N]; ll l, r, tot, dep[N]; bool stop[N], f[N]; //stop指那些无法到达根节点的子节点的军队停留的位置 vector<int> id[N]; //id存储每个根节点的子节点下的军队 vector<P> vec[N]; //该表存储的是每个根节点的子节点上驻扎的军队以及剩余时间 vector<int> A, B; //该表存储的是军队的剩余时间和每个军队到根节点后再到各个子节点的距离 int dfs1(int x,int root){ //一趟dfs获取深度,父节点等信息 if(id[x].size()){ for(int i = 0; i < id[x].size(); i++) dict[id[x][i]] = root; } for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(v == pre[x]) continue; pre[v] = x; e_pre[v] = i; dep[v] = dep[x] + edge[i].w; dfs1(v, root); } } bool dfs2(int x){ bool flag = 0; if(stop[x]) return 1; for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(v == pre[x]) continue; flag = 1; if(!dfs2(v)) return 0; } if(!flag) return 0; return 1; } void init(){ A.clear(); B.clear(); for(int i = 1; i <= n; i++) vec[i].clear(); memset(stop,0,sizeof(stop)); memset(f,0,sizeof(f)); } void work(int x,int lim){ while(edge[e_pre[x]].w <= lim){ lim -= edge[e_pre[x]].w; x = pre[x]; } stop[x] = 1; } bool cmp(P x, P y){ return x.second < y.second; } bool che(int lim){ init(); for(int i = 1; i <= m; i++){ int p = pos[i]; if(dep[p] - dep[dict[i]] > lim){ work(p, lim); continue; //暴力向上走 把军队驻扎到离根节点最近的位置 } vec[dict[i]].push_back(P(i, lim - (dep[p] - dep[dict[i]]))); } for(int i = head[1]; i; i = edge[i].next){ //判断根节点的每个子节点是否有病毒传播到叶子节点 f[edge[i].to] = dfs2(edge[i].to); } for(int i = head[1]; i; i = edge[i].next){ int v = edge[i].to; bool flag = 0; if(vec[v].empty()) if(!f[v]){ //如果该节点上没有军队只能靠其他军队救济 B.push_back(edge[i].w); continue; } sort(vec[v].begin(), vec[v].end(), cmp);//将每个点排序之后贪心地将剩余时间最短的军队驻扎在原地 int tmp = vec[v][0].second; //取出剩余时间最少的点 if(tmp - 2 * edge[i].w < 0 && !f[v]) flag = 1; //该节点已经被管制 else A.push_back(tmp - edge[i].w); for(int j = 1; j < vec[v].size(); j++){ int x = vec[v][j].first, t = vec[v][j].second;//将子节点上的军队全都调及到根节点 A.push_back(t - edge[i].w); } if(!flag && !f[v]) B.push_back(edge[i].w); } sort(A.begin(), A.end()); // 贪心扫描 剩余时间最少的军队去最近的子节点 sort(B.begin(), B.end()); int s = 0; for(int i = 0; i < A.size(); i++){ if(s == B.size()) break; if(A[i] >= B[s]) s++; } if(s == B.size()) return 1; else return 0; } int main(){ // freopen("testdata.in","r",stdin); scanf("%d",&n); int x, y, z; for(int i = 1; i < n; i++){ scanf("%d%d%d",&x, &y, &z); add(x, y, z); add(y, x, z); tot += z; } scanf("%d",&m); for(int i = 1; i <= m; i++) scanf("%d",&pos[i]), id[pos[i]].push_back(i); for(int i = head[1]; i; i = edge[i].next){ dep[edge[i].to] = edge[i].w; pre[edge[i].to] = 1, e_pre[edge[i].to] = i; dfs1(edge[i].to, edge[i].to); } r = tot; ll ans = 1e15; while(l <= r){ ll mid = l + r >> 1; if(che(mid)) ans = mid, r = mid - 1; else l = mid + 1; } if(ans == 1e15) puts("-1"); else printf("%lld\n",ans); return 0; }