[NOIP2012]疫情控制(二分答案+倍增+贪心)
Description
H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。 H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。 现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。 请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
\(Hint:2≤m≤n≤50,000,0 < w <10^9\)
Solution
可以发现,军队可以同时移动,那么就是求最大值最小,考虑二分答案,
接下来可以发现,军队越往上,控制的点越多,那么显然,军队应该尽量往上爬,
在树上向上爬,那就倍增吧(_)
所以用贪心的思路,所有军队尽量往根节点爬,然后再考虑爬到一些没有军队的分支,
那么如果去支援分支,显然只要到根节点的某个子节点就能控制这个分支,
将向上能到达根节点的点存起来,用B数组储存,并且记录它是从根节点的哪个子节点上来的,因为可能不去支援其他点,就要返回到上一个点
而到不了根节点的点,保持在最高点为最优,将这个最高点标记,表示这个点被控制了,此时只有部分点被标记为控制的,
此时进行一个操作,对于一个非根节点的点,如果其子节点全被控制,那它也是被控制的,就这样从根节点遍历整棵树,记录所有被控制的点,用C数组储存
然后再考虑让可以到达根节点的点去支援没有被控制的根节点的子节点
接下来将没有被控制的根节点的子节点也存起来,然后将BC,2个数组按到根节点的距离排序
注意是从小到大!因为B数组要先考虑要不要返回上一个点,
然后判断是否C数组所有点都可以被控制即可
(_)终于打完了,真的麻烦,细节挺多的,主要错误如下:
- 向上跳的时候,应先更新距离再更新点的编号
- 预处理倍增时,应从\(2^1\)开始算,因为\(2^0\)已经算过了
- !!!倍增最大可以到50000,但我看成5000,只开到\(2^{14}\),导致大数据挂
看来我倍增还是不熟练
Code
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 50010
using namespace std;
struct info {
int to, nex, w;
} e[N * 2];
int n, m, tot, head[N * 2], ans, arm[N], l, r;
int _log, f[N][20], dis[N][20];
struct node {
int d, fr;
} b[N], c[N];
bool flag[N];
bool cmp(node a, node b) {return a.d < b.d;}
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
inline void add_edge(int u, int v, int w) {
e[++tot].to = v;
e[tot].nex = head[u];
head[u] = tot;
e[tot].w = w;
}
void dfs(int u, int fa) {
for (int j = 1; j <= _log; ++j) {
f[u][j] = f[f[u][j - 1]][j - 1];
dis[u][j] = dis[u][j - 1] + dis[f[u][j - 1]][j - 1];
}
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
if (v == fa) continue;
f[v][0] = u;
dis[v][0] = e[i].w;
dfs(v, u);
}
}
void color(int u, int fa) {
int p = 1, q = 0;
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
if (v == fa) continue;
color(v, u);
p &= flag[v];
q = 1;
}
if (p && q && u != 1)
flag[u] = 1;
}
bool check(int mid) {
memset(flag, 0, sizeof(flag));
int cnt = 0, top = 0;
for (int i = 1; i <= m; ++i) {
int u = arm[i], cos = 0;
for (int j = _log; j >= 0; --j)
if (f[u][j] && dis[u][j] + cos <= mid) {
cos += dis[u][j];
u = f[u][j];
}
if (u != 1) flag[u] = 1;
else {
b[++cnt].d = mid - cos;
u = arm[i];
for (int j = _log; j >= 0; j--)
if (f[u][j] > 1)
u = f[u][j];
b[cnt].fr = u;
}
}
color(1, 0);
for (int i = head[1]; i; i = e[i].nex) {
int v = e[i].to;
if (!flag[v]) {
c[++top].d = e[i].w;
c[top].fr = e[i].to;
}
}
sort(b + 1, b + cnt + 1, cmp);
sort(c + 1, c + top + 1, cmp);
int j = 1; c[top + 1].d = 1e9;
for (int i = 1; i <= cnt; ++i) {
if (!flag[b[i].fr]) flag[b[i].fr] = 1;
else if (b[i].d >= c[j].d) flag[c[j].fr] = 1;
while (flag[c[j].fr]) j++;
}
return j > top;
}
inline void Init() {
n = read();
_log = log(n) / log(2);
for (int i = 1; i < n; ++i) {
int u = read(), v = read(), w = read();
r += w;
add_edge(u, v, w);
add_edge(v, u, w);
}
m = read();
for (int i = 1; i <= m; ++i)
arm[i] = read();
}
int main() {
Init();
dfs(1, 0);
ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid))
ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", ans);
return 0;
}
[NOIP2012]疫情控制(二分答案+倍增+贪心)的更多相关文章
- NOIP2012疫情控制(二分答案+树上贪心)
H 国有n个城市,这 n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示 ...
- luogu1084 [NOIp2012]疫情控制 (二分答案+倍增+dfs序)
先二分出一个时间,把每个军队倍增往上跳到不能再跳 然后如果它能到1号点,就记下来它跳到1号点后剩余的时间:如果不能,就让它就地扎根,记一记它覆盖了哪些叶节点(我在这里用了dfs序+差分,其实直接dfs ...
- NOIP2012疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- 洛谷P1084 疫情控制(NOIP2012)(二分答案,贪心,树形DP)
洛谷题目传送门 费了几个小时杠掉此题,如果不是那水水的数据的话,跟列队的难度真的是有得一比... 话说蒟蒻仔细翻了所有的题解,发现巨佬写的都是倍增,复杂度是\(O(n\log n\log nw)\)的 ...
- Luogu1084 NOIP2012D2T3 疫情控制 二分答案、搜索、贪心、倍增
题目传送门 题意太长就不给了 发现答案具有单调性(额外的时间不会对答案造成影响),故考虑二分答案. 贪心地想,在二分了一个时间之后,军队尽量往上走更好.所以我们预处理倍增数组,在二分时间之后通过倍增看 ...
- Luogu P1084 疫情控制 | 二分答案 贪心
题目链接 观察题目,答案明显具有单调性. 因为如果用$x$小时能够控制疫情,那么用$(x+1)$小时也一定能控制疫情. 由此想到二分答案,将问题转换为判断用$x$小时是否能控制疫情. 对于那些在$x$ ...
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...
- [NOIP2012]疫情控制 贪心 二分
题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...
- NOIP2012 疫情控制 题解(LuoguP1084)
NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...
随机推荐
- Redis中String类型的Value最大可以容纳数据长度
项目中使用redis存储,key-value方式,在Redis中字符串类型的Value最多可以容纳的数据长度是512M 官方信息: A String value can be at max 512 M ...
- SpringMVC03 ParameterMethodNameResolver(参数方法名称解析器) And XmlViewResolver(视图解析器)
参数方法名称解析器 1.配置依赖包 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht ...
- python中*args, **kwargs理解
先来看个例子: def foo(*args, **kwargs): print 'args = ', args print 'kwargs = ', kwargs print '----------- ...
- usb-host一步一步学(一)安卓在usb-host模式下列出当前连接的usb设备
在本次尝试中,我的安卓手机(HTC One X) 通过OTG线作为usb主机模式列出当前插入的usb设备,版本要求minSDKVersion="12". 没有外设的情况下,结果如下 ...
- npm warn weex @1.0.0 no repository field
玩weex出现nmp安装问题总是包这个错,但是其实是安装成功的 npm warn weex@1.0.0 no repository field. 看字面意思大概是package.json里缺少repo ...
- idea搭建ssm
第一步:打开intellij idea,创建maven项目 参考:http://blog.csdn.net/w8897282/article/details/71173211 1.创建一个maven ...
- OpenGL glReadPixels 转成 CImage
本来是一件很简单的事情,就是将View上使用OpenGL画出来的数据生成图片让其实现打印的功能,我们知道MFC提供打印的接口是不支持OpenGL的(至少我不清楚),必须将它转成GDI再画在Print的 ...
- OpenGL位图变形问题
因为初次接触OpenGL,图形学也后悔当初在学校没有认真学,隐约记得教授当时讲过图像变形的问题,而且我的bitmap也是2的N次方:16*16的,在网络上找到的大多都是一句话:“视口的纵横比一般和视景 ...
- IOS 网络-深入浅出(一 )
首要我们以最为常用的UIImageView为例介绍实现原理: 1)UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先显 ...
- 机器学习_线性回归和逻辑回归_案例实战:Python实现逻辑回归与梯度下降策略_项目实战:使用逻辑回归判断信用卡欺诈检测
线性回归: 注:为偏置项,这一项的x的值假设为[1,1,1,1,1....] 注:为使似然函数越大,则需要最小二乘法函数越小越好 线性回归中为什么选用平方和作为误差函数?假设模型结果与测量值 误差满足 ...