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

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

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

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

  3. Java小学四则运算

    本次作业要求来自:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2166 github远程仓库的地址:https://github.c ...

  4. centos7.5 搭建上FTP服务

    1.安装FTP # 查看ftp 是否安装 rpm -qa | grep vftpd # 安装vsftp yum install -y vsftpd # 设置ftp 开机启动 systemctl ena ...

  5. log4j2使用RollingFile重启丢失日志问题

    <RollingFile name="cnkiLogRollingFileError" fileName="${logbigdata.dir}/Log8080/er ...

  6. Nginx通过geo模式实现限速白名单和全局负载均衡 - 运维笔记

    Nginx的geo模块不仅可以有限速白名单的作用,还可以做全局负载均衡,可以要根据客户端ip访问到不同的server.比如,可以将电信的用户访问定向到电信服务器,网通的用户重 定向到网通服务器”,从而 ...

  7. 【转】用 async/await 来处理异步

    原文地址:https://www.cnblogs.com/SamWeb/p/8417940.html 昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简 ...

  8. Netty4实战 - TCP粘包&拆包解决方案

    Netty是目前业界最流行的NIO框架之一,它的健壮性.高性能.可定制和可扩展性在同类框架中都是首屈一指.它已经得到了成百上千的商业项目的验证,例如Hadoop的RPC框架Avro就使用了Netty作 ...

  9. 爬虫框架之selenium

    Selenium 一.概述 Web自动化测试工具,可以运行在浏览器,根据指令操作浏览器 只是工具,必须与第三方浏览器结合使用 安装: Linux:sudo pip3 install selenium ...

  10. MyBatis系列(二) MyBatis接口绑定与多参数传递

    前言 通过上一篇博文的,已经可以做到通过MyBatis连接数据库,现在再来介绍一种方法通过接口绑定SQL语句. 不使用接口绑定的方式 不使用接口绑定的方式,是通过调用SqlSession中的selec ...