洛谷P1084 疫情控制(贪心+倍增)
这个题以前写过一遍,现在再来写,感觉以前感觉特别不好写的细节现在好些多了,还是有进步吧。
这个题的核心思想就是贪心+二分。因为要求最小时间,直接来求问题将会变得十分麻烦,但是如果转换为二分答案来判断可行性,问题就会简化许多。
至于贪心的话,很容易发现每个点尽量往上面跳是最优的,这里向上跳的话我们用倍增来搞就是了。但题目中有个限制条件就是:根结点不能驻扎军队!!。那么我们就可以先让所有点尽量往上面跳,最多跳到 \(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 疫情控制(贪心+倍增)的更多相关文章
- 洛谷P1084 疫情控制(NOIP2012)(二分答案,贪心,树形DP)
洛谷题目传送门 费了几个小时杠掉此题,如果不是那水水的数据的话,跟列队的难度真的是有得一比... 话说蒟蒻仔细翻了所有的题解,发现巨佬写的都是倍增,复杂度是\(O(n\log n\log nw)\)的 ...
- 洛谷P1084 疫情控制 [noip2012] 贪心+树论+二分答案 (还有个小bugQAQ
正解:贪心+倍增+二分答案 解题报告: 正好想做noip的题目然后又想落实学长之前讲的题?于是就找上了这题 其实之前做过,70,然后实在细节太多太复杂就不了了之,现在再看一遍感觉又一脸懵了... 从标 ...
- 2018.09.26洛谷P1084 疫情控制(二分+倍增)
传送门 好题啊. 题目要求的最大值最小,看到这里自然想到要二分答案. 关键在于怎么检验. 显然对于每个点向根走比向叶节点更优. 因此我们二分答案之后,用倍增将每个点都向上跳到跳不动为止. 这时我们ch ...
- [NOIP2012] 提高组 洛谷P1084 疫情控制
题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散 ...
- NOIP2012 洛谷P1084 疫情控制
Description: H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情 ...
- 洛谷P1084 疫情控制
题目 细节比较多的二分+跟LCA倍增差不多的思想 首先有这样一个贪心思路,深度越低的检查点越好,而最长时间和深度具有单调性,即给定时间越长,每个军队能向更浅的地方放置检查点.因此可以考虑二分时间,然后 ...
- 洛谷 P1084 疫情控制 —— 二分+码力
题目:https://www.luogu.org/problemnew/show/P1084 5个月前曾经写过一次,某个上学日的深夜,精疲力竭后只有区区10分,从此没管... #include< ...
- 洛谷P1084 [NOIP2012提高组Day2T3]疫情控制
P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控 ...
- NOIP2012 D2 T3 疫情控制 洛谷P1084
题目链接:https://www.luogu.org/problemnew/show/P1084 算法:倍增,二分答案,贪心 + 瞎搞.. 背景:上学长的数论课啥也听不懂,于是前去提高组找安慰.不巧碰 ...
随机推荐
- scala集合有哪些
不可变集合图示:
- kubernetes学习一:安装及部署第一个Web应用
准备工作 首先准备Kubernets的环境,使用的是centos7.5 关闭防火墙: # systemctl disable firewalld # systemctl stop firewalld ...
- .net core 在 View 中使用 Jquery 无效问题
问题描述: 在 View 视图中使用模板 _Layout.cshtml,其中模板已经调用了 Jquery.js ,但是在 View 视图下写 js 无效.后来通过浏览器查看自己写的 js 压根没加载出 ...
- Matlab匿名函数
Matlab可以通过function去定义一些功能函数,这使得代码变得简洁和高效.但是如果遇到的是一些简单的数学公式组成的函数表达式,继续用function去定义函数,似乎显得有些冗杂和多余.这时候, ...
- ZYNQ笔记(5):软中断实现核间通信
ZYNQ包括一个 FPGA 和两个 ARM,多个 ARM 核心相对独立的运行不同的任务,每个核心可能运行不同的操作系统或裸机程序,但是有一个主要核心,用来控制整个系统以及其他从核心的允许.因此我们可以 ...
- ubuntu18 docker中部署ELK
ELK是ElasticSearch.Logstash.Kibana的简称,一般用于日志系统,从日志收集,日志转储,日志展示等入手,用以提供简洁高效的日志处理机制. 鉴于没有额外的机器,这里就用dock ...
- MQTTv5.0 ---AUTH – 认证交换
AUTH报文被从客户端发送给服务端,或从服务端发送给客户端,作为扩展认证交换的一部分,比如质询/ 响应认证.如果CONNECT报文不包含相同的认证方法,则客户端或服务端发送AUTH报文将造成协议错 误 ...
- 服务器同时安装python2支持的py-faster-rcnn以及python3支持的keras
最近把服务器折腾一下,搞定这两个.
- java异常的基本概念和处理流程
一.异常的基本概念 在java中把导致程序中断运行的情况分为两种,一种就是异常,而另外一种叫做错误.所有异常的基类是Exception,错误的基类是Error.Exception是在java程序中可以 ...
- .net Core 解决Form value count limit 1024 exceeded. (文件上传过大)
异常清空如图 原因:.net core提交的表单限制太小导致页面表单提交失败 在控制器上使用 RequestFormLimits attribute [RequestFormLimits(ValueC ...