[NOIP2012提高组]疫情控制

题意:在一棵树上,每条边有距离,有若干个军队,每秒走一单位距离,可以往各个节点移动,但最后不能停在1号节点。求覆盖所有叶节点的最短时间是多少。

为了优化大家的阅读感受,先放题解标程,再放解题过程&部分分

首先,军队一定是往根节点走,这样可以使覆盖的叶节点尽可能多,所以要贪心。要使时间最小,就要二分时间再验证答案。

这题本人觉得细节贼多,以至于被连续坑。譬如:有的军队还必须要经过根节点1,再到1的一些子节点。关键如何判断还是个问题。

本题代码如果看不懂没关系(码力欠缺,代码冗长),下面有详细证明

代码

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 
  6 using namespace std;
  7 
  8 #define re register
  9 #define LL long long
 10 #define rep(i, x, y) for (register int i = x; i <= y; ++i)
 11 #define repd(i, x, y) for (register int i = x; i >= y; --i)
 12 #define maxx(a, b) a = max(a, b)
 13 #define minn(a, b) a = min(a, b)
 14 #define inf 1e9
 15 #define linf 1e14
 16 
 17 inline int read() {
 18     int w = 0, f = 1; char c = getchar();
 19     while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
 20     while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ 48), c = getchar();
 21     return w * f;
 22 }
 23 
 24 const int maxn = 5e4 + 5;
 25 
 26 int n, m;
 27 
 28 struct Edge {
 29     int u, v, w, pre;
 30 };
 31 
 32 struct Gph {
 33     Edge edges[maxn << 1];
 34     int G[maxn], n, m;
 35     void init(int n) {
 36         this->n = n;
 37         m = 0;
 38         memset(G, 0, sizeof(G));
 39     }
 40     void AddEdge(int u, int v, int w) {
 41         edges[++m] = (Edge){u, v, w, G[u]};
 42         G[u] = m;
 43     }
 44 } G;
 45 
 46 struct Node {
 47     int d, p;
 48 } a[maxn], root[maxn];
 49 
 50 int cnt = 0, isarmy[maxn], label[maxn];
 51 LL minT[maxn], dist[maxn];
 52 
 53 bool cmp1(Node a, Node b) {
 54     return a.d < b.d;
 55 }
 56 
 57 bool cmp2(Node a, Node b) {
 58     return a.d > b.d;
 59 }
 60 
 61 int vis[maxn], cov[maxn];
 62 
 63 struct Tree {
 64     int lab;
 65     void dfs(int u) {
 66         if (isarmy[u]) minT[u] = 0;
 67         else minT[u] = linf;
 68         label[u] = lab;
 69         vis[u] = 1;
 70         for (re int i = G.G[u]; i; i = G.edges[i].pre) {
 71             Edge &e = G.edges[i];
 72             if (vis[e.v]) continue;
 73             dist[e.v] = dist[u] + e.w;
 74             dfs(e.v);
 75             minn(minT[u], minT[e.v] + e.w);
 76         }
 77     }
 78     void build() {
 79         memset(vis, 0, sizeof(vis));
 80         vis[1] = 1;
 81         for (re int i = G.G[1]; i; i = G.edges[i].pre) {
 82             Edge &e = G.edges[i];
 83             lab = e.v;
 84             root[++cnt].p = e.v;
 85             root[cnt].d = e.w;
 86             dist[e.v] = e.w;
 87             dfs(e.v);
 88         }
 89     }
 90     void dfs_root(int u, int TIME) {
 91         vis[u] = 1;
 92         if (cov[u]) {
 93             minT[u] = 0;
 94             return;
 95         } else minT[u] = linf;
 96         cov[u] = 1;
 97         bool flag = 1;
 98         for (re int i = G.G[u]; i; i = G.edges[i].pre) {
 99             Edge &e = G.edges[i];
100             if (vis[e.v]) continue;
101             flag = 0;
102             dfs_root(e.v, TIME);
103             cov[u] &= cov[e.v];
104             minn(minT[u], minT[e.v] + e.w);
105         }
106         if (flag) cov[u] = 0;
107         if (minT[u] <= TIME) cov[u] = 1;
108     }
109 } T;
110 
111 bool check(LL TIME) {
112     int g[maxn], g_cnt = 0, h[maxn], use[maxn], t = 0;
113     memset(g, 0, sizeof(g));
114     memset(h, 0, sizeof(h));
115     memset(cov, 0, sizeof(cov));
116     memset(vis, 0, sizeof(vis));
117     memset(use, 0, sizeof(use));
118     vis[1] = 1;
119     rep(i, 1, m)
120         if (a[i].d <= TIME) h[label[a[i].p]] = i, t = i; else cov[a[i].p] = 1;
121      
122      int g_p = 0;
123      use[0] = 1;
124     rep(i, 1, cnt) {
125         T.dfs_root(root[i].p, TIME);
126         if (cov[root[i].p]) continue;
127         if (use[h[root[i].p]]) {
128             for (g_p++; g_p <= t && (a[g_p].d + root[i].d > TIME || use[g_p]); g_p++) ;
129             if (g_p > t) return false;
130             use[g_p] = 1;
131         } else use[h[root[i].p]] = 1;
132     }
133     return g_p <= t ? 1 : 0;
134 }
135 
136 int main() {
137     n = read();
138 
139     rep(i, 1, n-1) {
140         re int u = read(), v = read(), w = read();
141         G.AddEdge(u, v, w);
142         G.AddEdge(v, u, w);
143     }
144 
145     m = read();
146 
147     rep(i, 1, m) a[i].p = read(), isarmy[a[i].p] = 1;
148 
149     T.build();
150 
151     rep(i, 1, m) a[i].d = dist[a[i].p];
152 
153     sort(a + 1, a + m + 1, cmp1);
154     sort(root + 1, root + cnt + 1, cmp2);
155 
156     LL l = 0, r = linf;
157     while (l < r) {
158         LL mid = l + r >> 1;
159         if (check(mid)) r = mid;
160         else l = mid+1;
161     }
162 
163     if (l == linf) printf("-1");
164     else printf("%lld", l);
165 
166     return 0;
167 }

题解

上面已经说过,想要覆盖更多叶节点,必须往根走。因为不能停在1号节点,暂时不考虑军队过首都到别的地方,问题可以看成:1号节点下有若干节点,只要判断每一个节点的子树的叶节点是否被覆盖即可。

先做些记号:

$a[i] = \{dist,\ p\}$ $a$是$Army$军队的缩写,其中有两个量:$dist$表示该军队到根节点的距离,$p$表示该军队初始所在的节点。

$root[i] = \{dist,\ p\}$ 与$a$类似,每一个表示的是1号节点的子节点到1号点的距离和该子节点的编号。等于单独把子节点提出来当作若干根节点,之后统一称作$root$。

$cnt$ 表示$root$的大小。

$cov[i]$ 表示第$i$个节点是否被军队覆盖,1表示被覆盖。 请注意:这个$cov$是会变动的(之后你就知道了)

$minT[i]$ 表示在覆盖情况为$cov$的情况下,第$i$个节点被覆盖的最小时间。

二分$Ans$,再判断。如何判断?有一种方法是通过倍增,每次将这$n$个军队提前跳,再dfs判断是否覆盖。当然也行。不过我这里用的是更高效的方法。将所有的$root$ dfs一遍,求$minT$并通过$minT$来判断是否覆盖。不难可以得出:$minT[u]=minT[v]+w[u,v]\ |\ v\in u$的子节点,初始值:当$cov[u]=1$时,$minT[u]=0$,否则$minT[u]=INF$。判断覆盖的话,如果$minT[u]\leq Ans$,则说明一定有军队到这个点,即$u$点以下的叶节点全部能被覆盖,或者递归$u$全部的子节点,如果全满足时,也可以全部覆盖,否则就不行。

加上复杂的情况:某些$root$子树中不存在军队。

怎么办?

从别的地方调呗!

最后判断一下即可。

posted @ 2018-10-21 12:29  AC-Evil  阅读(443)  评论(0编辑  收藏  举报