您的位置:首页 > 其它

codevs1218: [NOIP2012]疫情控制

2016-10-24 21:52 267 查看
题目链接

在一个有n个节点的带权树上除根节点外的某些节点驻扎着共m个军队。令这些军队沿着树边移动,最终使得每条从根节点到叶子节点的路径上(根节点除外)至少驻扎有1个军队,求所有军队移动的总路程的最小值。

很容易想到二分答案判断可行性,移动策略如下。

在给定移动距离上限的情况下,尽量向上移动是最优的,除在根节点以外的向下移动都是无意义的。只向上移动而不经过根节点的情况可以直接用倍增解决,然而经过根节点的情况需要进一步使用贪心决策。排序后依次判断,若该军队所处路径没有被覆盖,则直接覆盖该路径,否则判断是否可覆盖其他未被覆盖的离根节点最近的路径。

贴上调了一下午的丑比代码。

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 50050, H = 16;
typedef long long ll;
inline int read() {
char c = getchar();
int rst = 0;
while (c < '0' or  c > '9') c = getchar();
while (c >= '0' and c <= '9') {
rst *= 10; rst += c - '0'; c = getchar();
}
return rst;
}

struct edge{
int v, w;
edge(int __v, int __w): v(__v), w(__w) {}
};
vector<edge> E
;
int n, m, army
, fa
;
ll dist
, maxDist;

void dfs(int u, int f) {
fa[u] = f;
for (int i = 0; i < E[u].size(); i++) {
int v = E[u][i].v, w = E[u][i].w;
if (v == f) continue;
dist[v] = dist[u] + (ll)w;
dfs(v, u);
}
}

int up
[H];
inline void initLca() {
for (int i = 1; i <= n; i++)
up[i][0] = fa[i];
for (int j = 1; j < H; j++) {
bool flag = false;
for (int i = 1; i <= n; i++)
if (up[i][j - 1]) {
up[i][j] = up[up[i][j - 1]][j - 1];
flag = true;
}
if (! flag) break;
}
}

inline int findR(int u) {
int p = u;
while (fa[p] != 1) {
for (int j = 1; j < H; j++) {
if (! up[p][j] or up[p][j] == 1) {
p = up[p][j - 1]; break;
}
}
} return p;
}

inline int getFa(int u, ll cap) {
if (cap >= dist[u]) return findR(u);
int p = u;
while (dist[u] - dist[fa[p]] <= cap) {
if (dist[u] - dist[fa[p]] == cap) return fa[p];
for (int j = 1; j < H; j++) {
if (! up[p][j] or dist[u] - dist[up[p][j]] > cap) {
p = up[p][j - 1]; break;
}
}
} return p;
}

bool mark
;
int son
, spare
, sCnt, need
, nCnt;
void fill(int u) {
if (mark[u]) return;
mark[u] = true;
if (! fa[u]) return;
son[fa[u]]++;
if (! mark[fa[u]] and son[fa[u]] == E[fa[u]].size() - 1 + (fa[u] == 1))
fill(fa[u]);
}

inline bool cmp(int a, int b) {
return dist[a] > dist[b];
}

inline bool cmp2(int a, int b) {
return dist[a] < dist[b];
}

inline bool judge(ll cap) {
memset(mark, 0, sizeof(mark));
memset(son, 0, sizeof(son));
nCnt = sCnt = 0;
for (int i = 1; i <= m; i++) {
int now = army[i];
if (dist[now] < cap) {
spare[++sCnt] = now;
} else {
int p = getFa(now, cap);
fill(p);
}
}
if (mark[1]) return true;
sort(spare + 1, spare + 1 + sCnt, cmp);
for (int i = 0; i < E[1].size(); i++)
if (! mark[E[1][i].v]) need[++nCnt] = E[1][i].v;
sort(need + 1, need + 1 + nCnt, cmp2);
for (int i = 1, j = 1; i <= sCnt; i++) {
int rt = findR(spare[i]);
if (! mark[rt]) fill(rt);
else if (cap - dist[spare[i]] >= dist[need[j]]) fill(need[j]);
while (mark[need[j]] && j <= nCnt) j++;
if (j >= nCnt + 1) return true;
}
return false;
}

inline ll work() {
if (m < E[1].size()) return -1LL;
ll l = 0, r = (maxDist << 1) * m;
while (l < r) {
ll mid = l + r >> 1;
if (judge(mid)) r = mid;
else l = mid + 1;
} return l;
}

int main() {
n = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read(), w = read();
E[u].push_back(edge(v, w));
E[v].push_back(edge(u, w));
} dfs(1, 0); initLca();
m = read();
for (int i = 1; i <= m; i++) {
scanf("%d", &army[i]);
maxDist = max(maxDist, dist[army[i]]);
}
printf("%lld\n", work());
return 0;
}


这是一篇意义不明的无良题解,蒟蒻博主只是想吐槽这道题的变态出题人。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  贪心 倍增