codevs 1218 疫情控制
题意:
一棵有n个点的树,树上有m个障碍在给定的位置,现在需要移动这m个障碍,使得从根节点出发的任意路径不能到达任意一个叶子节点(不能放在根节点上),求移动的最小花费。
题解:
首先需要明白的有这几点:
① 如果一个点所有的儿子都被阻碍了,那么这个点也会被阻碍,那么现在的问题就是覆盖根所有的儿子节点。
② 若在规定的时间移动这m个点,那么这m个点越往上移动越优越。
③ 若在规定的时间移动这m个点,那么一定会出现两种情况,第一种不能越过根节点,第二种能够越过根节点。
现在需要解决一下问题:
1.怎么解决规定的时间,可以二分一个时间。
2.怎么在很小的复杂度使得m个点尽量的往上跳,倍增
3.怎么处理那两种情况?
首先处理不能越过根节点的情况,尽量的往上跳,把到达极限的点标记,然后将所有的标记根据①的原理上传到根的儿子。
然后处理能够越过根节点的情况,记录下每个能够越过根节点的点剩余的时间 t 和属于哪条路径的根的儿子的编号 id,记录下需要被其他路径的点覆盖的根的儿子的编号,然后贪心覆盖就好了。。。
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 5e4 + 7; struct edge {int v, nxt, w;}e[N<<1]; struct node {int w, id;} g[N], f[N]; int anc[20][N], cos[20][N], ecnt, head[N], vis[N], n, m, army[N]; void adde (int u, int v, int w) { e[ecnt].v = v; e[ecnt].w = w; e[ecnt].nxt = head[u]; head[u] = ecnt++; } void DFS (int u, int pre) { for (int it = head[u]; it != -1; it = e[it].nxt) { int v = e[it].v; if (v == pre) continue; anc[0][v] = u; cos[0][v] = e[it].w; for (int i = 1; i <= 18; ++i) { anc[i][v] = anc[i-1][anc[i-1][v]]; cos[i][v] = cos[i-1][v] + cos[i-1][anc[i-1][v]]; } DFS(v, u); } } bool cmp (node a, node b) {return a.w < b.w;} void pushup (int u, int pre) { int isleaf = 1, flag = 1; for (int it = head[u]; it != -1; it = e[it].nxt) { int v = e[it].v; if (v == pre) continue; pushup(v, u); if (!vis[v]) flag = 0; isleaf = 0; } if (flag && !isleaf && u != 1) vis[u] = 1; } int check (int x) { int cntg = 0, cntf = 0; memset (vis, 0, sizeof vis); for (int i = 1; i <= m; ++i) { int p = army[i], t = x; for (int j = 18; j >= 0; --j) { if (anc[j][p] && t >= cos[j][p]) { t -= cos[j][p]; p = anc[j][p]; } } if (p == 1) { p = army[i]; for (int j = 18; j >= 0; --j) if (anc[j][p] > 1) p = anc[j][p]; g[++cntg] = (node){t, p}; } else vis[p] = 1; } pushup(1, 0); for (int it = head[1]; it != -1; it = e[it].nxt) { int v = e[it].v; if (!vis[v]) f[++cntf] = (node){e[it].w, v}; } sort (g + 1, g + 1 + cntg, cmp); sort (f + 1, f + 1 + cntf, cmp); int cur = 1; f[cntf+1] = (node){1e9 + 7, 0}; for (int i = 1; i <= cntg; ++i) { if (!vis[g[i].id]) vis[g[i].id] = 1; else if (g[i].w >= f[cur].w) vis[f[cur].id] = 1; while (vis[f[cur].id]) cur++; } return cur > cntf; } int main () { memset (head, -1, sizeof head); scanf ("%d", &n); for (int i = 1; i < n; ++i) { int u, v, w; scanf ("%d%d%d", &u, &v, &w); adde (u, v, w); adde (v, u, w); } DFS (1, 0); scanf ("%d", &m); for (int i = 1; i <= m; ++i) scanf ("%d", &army[i]); int l = 0, r = 1e9 + 7; while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } if (l == 1e9 + 7) puts("-1"); else printf ("%d\n", l); return 0; }
总结:
遇到问题不要慌张~慢慢分析,首先不要考虑复杂度,直接嘴炮,然后慢慢进行算法上的优化。。。