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

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

至于贪心的话,很容易发现每个点尽量往上面跳是最优的,这里向上跳的话我们用倍增来搞就是了。但题目中有个限制条件就是:根结点不能驻扎军队!!。那么我们就可以先让所有点尽量往上面跳,最多跳到 \(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. Redis哨兵、复制、集群的设计原理与区别

    一 前言 谈到Redis服务器的高可用,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. 哨兵(Sentinel):可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转 ...

  2. AQS源码的简单理解

    概念 AQS全称 AbstractQueuedSynchronizer. AQS是一个并发包的基础组件,用来实现各种锁,各种同步组件的.它包含了state变量.加锁线程.等待队列等并发中的核心组件. ...

  3. 修改mysql端口后重启mysql报错:Can't start server: Bind on TCP/IP port. Got error...n denied

    1:错误信息:如下 [root@host ~]# systemctl status mariadb ● mariadb.service - MariaDB database server Loaded ...

  4. PHP strlen 对中文 的定义

    $zhStr = ‘您好,中国!’; // gbk编码下每个中文字符所占字节为2echo strlen($zhStr); // 输出:12 //utf-8编码下每个中文字符所占字节为3 echo st ...

  5. Alpha冲刺(11/10)——2019.5.3

    作业描述 课程 软件工程1916|W(福州大学) 团队名称 修!咻咻! 作业要求 项目Alpha冲刺(团队) 团队目标 切实可行的计算机协会维修预约平台 开发工具 Eclipse 团队信息 队员学号 ...

  6. JMeter工具学习(一)工具使用详细介绍

    备注: JMeter版本4.0 JDK版本1.8 1,JMeter下载 2,下载后直接解压 3,打开解压文件,找到bin目录下的jmeter.bat,双击打开 4,打开jmeter 6,右键Test ...

  7. mount和umount命令使用

    1.mount命令 在Linux系统下,mount命令用于加载文件系统到指定的加载点. (1)命令语法 mount(选项)(参数) (2)常用选项说明 -V:显示版本. -l:显示已加载的文件系统列表 ...

  8. Java 中常见的 final 类

    Java 中常见的 final 类 java.lang 包 public final class Boolean extends Object implements Serializable, Com ...

  9. TF-IDF算法简析

    TF-IDF算法可用来提取文档的关键词,关键词在文本聚类.文本分类.文献检索.自动文摘等方面有着重要应用. 算法原理 TF:Term Frequency,词频 IDF:Inverse Document ...

  10. 【题解】Kathy函数 [BZOJ1223] [P2235] [HNOI2002]

    [题解]Kathy函数 [BZOJ1223] [P2235] [HNOI2002] 这几疯狂刷了数位\(dp\)的题,到这道题时被卡了一天,一看大佬的讲解发现居然是求回文数╮(╯_╰)╭ 感觉被大佬狠 ...