这个题以前写过一遍,现在再来写,感觉以前感觉特别不好写的细节现在好些多了,还是有进步吧。

这个题的核心思想就是贪心+二分。因为要求最小时间,直接来求问题将会变得十分麻烦,但是如果转换为二分答案来判断可行性,问题就会简化许多。

至于贪心的话,很容易发现每个点尽量往上面跳是最优的,这里向上跳的话我们用倍增来搞就是了。但题目中有个限制条件就是:根结点不能驻扎军队!!。那么我们就可以先让所有点尽量往上面跳,最多跳到 \(deep[x]=2\) 的位置。之后考虑题目中的限制条件怎么解决。

比较直接的想法就是所有点都跳到根节点,然后贪心进行“分配”。但这里有个问题,有些点跳到根节点回不来怎么办?那么此时我们就可以大胆猜想:留住一个回不去的深度为2的结点,其余点就像之前那样贪心进行分配。

为什么这样是对的,给出一个简略证明:

如果一个点 \(x\) 跳到根节点但无法回去,此时另外一个点 \(y\) 来管辖 \(x\) 之前的所在的子树, \(x\) 现在可能去管辖另外一个以 \(z\) 为根节点的子树,满足 \(dis(z,root)<dis(x,root)\) 。我们很容易知道, \(y\) 肯定也能去管辖 \(z\) 所在子树的。因为 \(y\) 能够移动的距离更长,所以 \(y\) 对于 \(x\) 有更多的可能性(前途更加光明)

因此可以知道我们的贪心策略不会使答案变差的,那思路就是这样了。

至于代码细节,首先第一次dfs预处理出倍增的信息,之后二分进行check,里面嵌套一个dfs求出哪些深度为2的结点留住了军队。最后其余的点贪心去分配就行了。中途维护的信息可能有点多,多开几个数组维护一下= =。

代码如下(自认为比较好懂)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 50005;
int n, m;
int a[N], c[N];
int f[N][20], deep[N] ;
ll dis[N][20];
struct Edge{
int v, next, w;
}e[N << 1];
struct Army{
int id;
ll res;
bool operator < (const Army &A)const {
return res < A.res ;
}
}b[N], d[N], k[N];
int head[N], tot;
void adde(int u, int v, int w) {
e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
}
void dfs1(int u, int fa) {
deep[u] = deep[fa] + 1;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v == fa) continue ;
dis[v][0] = e[i].w;
f[v][0] = u;
for(int j = 1; j <= 17; j++) {
f[v][j] = f[f[v][j - 1]][j - 1] ;
dis[v][j] = dis[v][j - 1] + dis[f[v][j - 1]][j - 1] ;
}
dfs1(v, u) ;
}
}
bool vis[N] ;
vector <int> node[N];
int cnt, num;
bool dfs2(int u, int fa) {
int sz = node[u].size();
bool ok = 1, in = 0;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v == fa) continue ;
in = 1;
if(!dfs2(v, u)) ok = 0;
}
if(!in) ok = 0;
if(deep[u] == 2 && ok) vis[u] = 1;
if(deep[u] == 2 && sz) {
int st = 0;
if(!vis[u] && b[node[u][0]].res < 2 * dis[u][0]) st++, vis[u] = 1;
for(int i = st; i < sz; i++) {
d[++cnt].id = b[node[u][i]].id;
d[cnt].res = b[node[u][i]].res;
}
}
return ok || sz;
}
bool check(ll x) {
for(int i = 1; i <= n; i++) node[i].clear(), vis[i] = 0;
for(int i = 1; i <= m; i++) b[i].id = a[i] ;
for(int i = 1; i <= m; i++) {
ll res = x;
int now = b[i].id;
for(int j = 17; j >= 0; j--) {
if(deep[f[now][j]] >= 2 && dis[now][j] <= res) {
res -= dis[now][j] ;
now = f[now][j] ;
}
}
b[i].id = now; b[i].res = res ;
}
num = cnt = 0;
sort(b + 1, b + m + 1);
for(int i = 1; i <= m; i++) node[b[i].id].push_back(i) ;
dfs2(1, 0) ;
for(int i = 1; i <= n; i++)
if(deep[i] == 2 && !vis[i])
k[++num].res = dis[i][0], k[num].id = i;
for(int i = 1; i <= cnt; i++) {
int now = d[i].id ;
if(dis[now][0] < d[i].res) d[i].res -= dis[now][0];
else d[i].res = 0;
}
sort(d + 1, d + cnt + 1) ;
sort(k + 1, k + num + 1) ;
if(cnt < num) return false;
for(int i = 1, j = 1; i <= cnt; i++) {
if(d[i].res >= k[j].res) {
vis[k[j].id] = 1;
j++;
}
}
bool ok = 1;
for(int i = 1; i <= n; i++)
if(deep[i] == 2 && !vis[i]) ok = 0;
return ok;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0) ;
cin >> n;
memset(head, -1, sizeof(head)) ;
ll l = 0, r = 0, mid;
for(int i = 1; i < n; i++) {
int u, v, w;
cin >> u >> v >> w;
adde(u, v, w); adde(v, u, w) ;
r += w;
}
ll tmp = r;
dfs1(1, 0) ;
cin >> m;
for(int i = 1; i <= m; i++) cin >> a[i] ;
while(l < r) {
mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
if(l == tmp) cout << -1;
else cout << r ;
return 0;
}

洛谷P1084 疫情控制(贪心+倍增)的更多相关文章

  1. 洛谷P1084 疫情控制(NOIP2012)(二分答案,贪心,树形DP)

    洛谷题目传送门 费了几个小时杠掉此题,如果不是那水水的数据的话,跟列队的难度真的是有得一比... 话说蒟蒻仔细翻了所有的题解,发现巨佬写的都是倍增,复杂度是\(O(n\log n\log nw)\)的 ...

  2. 洛谷P1084 疫情控制 [noip2012] 贪心+树论+二分答案 (还有个小bugQAQ

    正解:贪心+倍增+二分答案 解题报告: 正好想做noip的题目然后又想落实学长之前讲的题?于是就找上了这题 其实之前做过,70,然后实在细节太多太复杂就不了了之,现在再看一遍感觉又一脸懵了... 从标 ...

  3. 2018.09.26洛谷P1084 疫情控制(二分+倍增)

    传送门 好题啊. 题目要求的最大值最小,看到这里自然想到要二分答案. 关键在于怎么检验. 显然对于每个点向根走比向叶节点更优. 因此我们二分答案之后,用倍增将每个点都向上跳到跳不动为止. 这时我们ch ...

  4. [NOIP2012] 提高组 洛谷P1084 疫情控制

    题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散 ...

  5. NOIP2012 洛谷P1084 疫情控制

    Description: H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情 ...

  6. 洛谷P1084 疫情控制

    题目 细节比较多的二分+跟LCA倍增差不多的思想 首先有这样一个贪心思路,深度越低的检查点越好,而最长时间和深度具有单调性,即给定时间越长,每个军队能向更浅的地方放置检查点.因此可以考虑二分时间,然后 ...

  7. 洛谷 P1084 疫情控制 —— 二分+码力

    题目:https://www.luogu.org/problemnew/show/P1084 5个月前曾经写过一次,某个上学日的深夜,精疲力竭后只有区区10分,从此没管... #include< ...

  8. 洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

    P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控 ...

  9. NOIP2012 D2 T3 疫情控制 洛谷P1084

    题目链接:https://www.luogu.org/problemnew/show/P1084 算法:倍增,二分答案,贪心 + 瞎搞.. 背景:上学长的数论课啥也听不懂,于是前去提高组找安慰.不巧碰 ...

随机推荐

  1. Oracle_创建自增

    create sequence SEQ_ChamberMapping_ID minvalue maxvalue start with increment by nocache order; CREAT ...

  2. Python(二)Marshmallow 库相关学习

    0. 前言 Marshmallow 是一个用于将 ORM 对象与 Python 原生数据类型之间转换的库.实现 object → dict.object → list.string → dict 和 ...

  3. Expect Command And How To Automate Shell Scripts Like Magic

    In the previous post, we talked about writing practical shell scripts and we saw how it is easy to w ...

  4. C#内存泄露与资源释放 经验总结

    本文链接:http://blog.csdn.net/yokeqi/article/details/41083939 C#相比其他语言,拥有强大的垃圾回收机制,但并不是这样,你就可以对内存管理放任不管, ...

  5. sonarqube使用maven进行代码分析

    修改setting.xml文件,增加并激活profile <profile> <id>sonar</id> <properties> <sonar ...

  6. javascript中的each遍历

    each的用法  1.数组中的each 复制代码 var arr = [ "one", "two", "three", "four ...

  7. storm单机运行报错 ERROR backtype.storm.daemon.executor -

    单机本地运行storm报错: 错误如下: java.lang.NullPointerException: null at test2.Spot2.nextTuple(Spot2.java:) ~[cl ...

  8. 017 使用域名访问本地项目---涉及switchhost和Nginx

    1.统一环境 我们现在访问页面使用的是:http://localhost:9001 实际开发中,会有不同的环境: 开发环境:自己的电脑 测试环境:提供给测试人员使用的环境 预发布环境:数据是和生成环境 ...

  9. 算法设计与分析(李春保)练习题答案v1

    1.1第1 章─概论 1.1.1练习题 1.下列关于算法的说法中正确的有(). Ⅰ.求解某一类问题的算法是唯一的 Ⅱ.算法必须在有限步操作之后停止 Ⅲ.算法的每一步操作必须是明确的,不能有歧义或含义模 ...

  10. SQL系列(三)—— 注释(comment)

    SQL语句是由DBMS处理的指令.如果你希望包括不进行处理和执行的文本,该怎么办呢?为什么你想要这么做呢?原因有以下几 点. 我们这里使用的SQL语句都很短,也很简单.然而,随着你的SQL语句变长,复 ...