P1084 [NOIP2012 提高组] 疫情控制
先二分
- 将所有军队尽量往上跳
- 如果能跳到根节点的子节点且能够越过根节点,存入pair(到根节点后的剩余时间,节点编号)(使用倍增优化) ; 否则:驻扎此节点
- 将pair的点按第一个排序
- 用第一个最小覆盖第一个没有被覆盖的根节点的子节点(贪心)
- 剩下的节点排序后使用双指针判断是否有解
点击查看代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int N = 5e4 + 5, M = N << 1, logN = 16;
typedef long long LL;
int n, m;
int h[N], e[M], w[M], nxt[M], idx;
int f[N][logN], dep[N];
LL dist[N][logN];
int armies[N];
void add(int a, int b, int c) {
e[++ idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx;
}
void dfs1(int u) {
for(int i = 1; i < logN && f[u][i-1]; i ++)
f[u][i] = f[f[u][i-1]][i-1], dist[u][i] = dist[f[u][i-1]][i-1] + dist[u][i-1];
for(int i = h[u]; i; i = nxt[i]) {
int v = e[i];
if(v == f[u][0]) continue;
f[v][0] = u, dep[v] = dep[u] + 1, dist[v][0] = w[i], dfs1(v);
}
}
bool cov[N]; // 节点是否驻扎
bool need_cov[N]; // 节点(根节点的儿子)需要被驻扎
std::pair<LL, int> remain[N]; // 能够到达根节点的军队的(剩余时间,节点编号) 其余的直接使用
LL val1[N]; // 没有匹配的军队所有距离
LL val2[N]; // 没有匹配的点到根节点的距离
bool dfs2(int u) { // 子树是否全部驻扎
if(cov[u]) return true;
bool is_leaf = true; // 是否为叶节点
for(int i = h[u]; i; i = nxt[i]) {
int v = e[i];
if(v == f[u][0]) continue;
is_leaf = false;
if(!dfs2(v)) return false;
}
return !is_leaf;
}
bool check(LL lim) {
memset(cov + 1, 0, n);
int tot = 0, tot1 = 0, tot2 = 0;
for(int i = 1; i <= m; i ++) {
int u = armies[i]; LL now = 0;
for(int j = logN - 1; j >= 0; j --)
if(f[u][j] > 1 && now + dist[u][j] <= lim) now += dist[u][j], u = f[u][j];
if(f[u][0] == 1 && now + dist[u][0] <= lim) remain[tot ++] = {lim - now - dist[u][0], u};
else cov[u] = true; // 原地不动
}
for(int i = h[1]; i; i = nxt[i]) need_cov[e[i]] = !dfs2(e[i]);
std::sort(remain, remain + tot);
for(int i = 0; i < tot; i ++)
if(need_cov[remain[i].second] && remain[i].first < dist[remain[i].second][0])
need_cov[remain[i].second] = false; // 用最小的覆盖没有被覆盖的点
else val1[tot1 ++] = remain[i].first;
for(int i = h[1]; i; i = nxt[i])
if(need_cov[e[i]]) val2[tot2 ++] = w[i];
if(tot1 < tot2) return false;
std::sort(val1, val1 + tot1), std::sort(val2, val2 + tot2);
int i = 0, j = 0; // 双指针
while(i < tot1 && j < tot2)
if(val1[i] >= val2[j]) i ++, j ++;
else i ++;
return j >= tot2;
}
int main() {
scanf("%d", &n);
LL l = 0, r = 0; bool ok = false;
for(int i = 1, a, b, c; i < n; i ++)
scanf("%d%d%d", &a, &b, &c), add(a, b, c), add(b, a, c), r += c;
scanf("%d", &m);
for(int i = 1; i <= m; i ++) scanf("%d", armies + i);
dep[1] = 1, dfs1(1);
while(l < r) {
int mid = (l + r) >> 1;
if(check(mid)) r = mid, ok = true;
else l = mid + 1;
}
printf("%lld\n", ok ? l : -1LL);
return 0;
}