[NOIP2012]疫情控制 贪心 二分
题解:
大体思路很好想,但是有个细节很难想QAQ
首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log。
然后发现一个军队,越往上走肯定可以控制的叶节点越多,因此我们在时间范围内尽量向上走,又得到一个log了。
如果一个军队走到根后还有多余时间,那它就有可能走到根的其他儿子去帮助其他子树。
然后为了尽可能覆盖多的子树,显然应该要用剩余时间少的军队,对应走过去代价小的子树,所以sort一下就可以了?
然而还有一种情况,那就是一个点从它的子树出发到了root,万一最后需要回到它自己那个子树,直接做就把代价算了2次,这样就可能导致本来可以不花代价就回到原来的子树的,但我们却花了双倍代价。。。。于是可能就把一个合法的时间判成不合法了。
所以应该怎么做?
其实只需要在 每个子树中可以走出去帮助其他子树的军队 里面选一个剩余时间最少的,走出去不能回来的军队守护自己就可以了(前提是自己还需要守护)。
可以证明,这样肯定是最优的。
因为如果把所有军队都提上去的话,意味这你要用军队x来帮助其他子树,同时意味着要从其他子树选一个军队y来帮助它。那么观察到既然军队x出来后无法回去,却可以帮助某个子树u,因此到这个被帮助的子树u的代价要比回去的代价小。所以如果我们用军队x来守护自己所在的子树,那么原本从其他子树中选出来帮助它的军队y就可以去守护子树u,因为子树u代价比当前子树小,因此子树u一定可以被军队y守护到。
所以肯定不会变劣。
写的时候还有一些细节,,,
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 51000
#define ac 101000
#define LL long long int n, m, num, cnt, rnt;
LL all;
int Head[AC], Next[ac], date[ac], tot;
int p[AC], father[AC][];
LL st[AC][], have[AC], len[ac];
bool z[AC], used[AC], vis[AC]; struct node{
int x;
LL rest;
friend bool operator < (node a, node b){
return a.rest < b.rest;
}
}s[AC], son[AC]; inline int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline void add(int f, int w, int S)
{
date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S;
date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot, len[tot] = S;
} void pre()
{
n = read();
for(R i = ; i < n; i ++)
{
int a = read(), b = read(), c = read();
add(a, b, c), all += c;
}
father[][] = ;//父亲设为自己,防止子树里面的点跳到0
m = read();
for(R i = ; i <= m; i ++) p[i] = read();
} void dfs1(int x)//预处理倍增数组 get: st father have son
{
vis[x] = true;
for(R i = ; i <= ; i ++)
{
father[x][i] = father[father[x][i - ]][i - ];
st[x][i] = st[x][i - ] + st[father[x][i - ]][i - ];
}
int now;
for(R i = Head[x]; i; i = Next[i])
{
now = date[i];
if(vis[now]) continue;
if(x == ) son[++ num] = (node){now, len[i]};
father[now][] = x, st[now][] = len[i];
have[now] = have[x] + len[i], dfs1(now);//记录下从root到now的距离
}
} void dfs2(int x)//找到哪些节点还没有被控制
{
if(z[x]) return ;
int now;bool done = true, flag = false;
for(R i = Head[x]; i; i = Next[i])
{
now = date[i];
if(now == father[x][]) continue;
dfs2(now), flag = true;
if(!z[now]) done = false;//如果儿子里面有一个不合法的,这个节点就不合法
}//不能直接return,因为1号节点一般都不合法,但其他儿子还要标记的,,,
if(flag) z[x] = done;//否则所有儿子都合法,那这个点就合法,但是如果这个点是叶子,,,就不能平白无故打标记了
} bool check(int mid)//判断这个时间是否合法
{
cnt = rnt = ;
memset(z, , sizeof(z));
for(R i = ; i <= m; i ++)
{
int x = p[i], now = ;
for(R j = ; j >= ; j --)
if(father[x][j] != && now + st[x][j] <= mid)
now += st[x][j], x = father[x][j];//记得要先加后跳
if(have[x] >= mid - now) z[x] = true;//无法到达别的子树
else s[++ cnt] = (node){x, mid - now};//可以到达
}
dfs2();
//sort(s + 1, s + cnt + 1);
for(R i = ; i <= cnt; i ++)//分配一个不能回来的给当前子树
if(s[i].rest < * have[s[i].x] && !z[s[i].x]) z[s[i].x] = true;
else s[++ rnt] = s[i], s[rnt].rest -= have[s[i].x];//提到root的同时要加上去root的代价
sort(s + , s + rnt + );//排序。
int l = ;
for(R i = ; i <= num; i ++)//剩下的从小到大依次匹配
{
if(z[son[i].x]) continue;
while(s[l].rest < son[i].rest && l <= rnt) ++ l;
if(l > rnt) return false;
++ l;//把这个用了
}
return true;
} void half()//二分时间
{
if(num > m) {printf("-1\n"); return ;}//如果root儿子数大于军队数,那么永远不可能全部覆盖
sort(son + , son + num + );
int l = , r = all, mid;
while(l < r)
{
mid = (l + r) >> ;
if(check(mid)) r = mid;
else l = mid + ;
}
printf("%d\n", l);
} int main()
{
freopen("in.in", "r", stdin);
pre();
dfs1();
half();
fclose(stdin);
return ;
}
[NOIP2012]疫情控制 贪心 二分的更多相关文章
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...
- NOIP2012疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- [NOIP2012]疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- NOIP2012 疫情控制 题解(LuoguP1084)
NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...
- 【NOIP2012】疫情控制(二分,倍增,贪心)
洛谷上的题目链接,题目不在赘述 题解 既然要时间最短,首先考虑二分. 因此,考虑二分时间,问题转换为如何检查能否到达. 如果一支军队一直向上走,能够到达根节点,那么他可以通过根节点到达其他的节点,因此 ...
- NOIP2012疫情控制(二分答案+树上贪心)
H 国有n个城市,这 n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示 ...
- P1084 [NOIP2012 提高组] 疫情控制 (二分答案、贪心)
因为若一个时间限制满足题意,则所有比它大的时间限制一定都满足题意,因此本题答案具有单调性,可以想到二分答案求解. 本题思路不是很难,但细节和代码实现比较复杂. 见牛人博客:https://www.lu ...
- NOIP 2012 疫情控制(二分+贪心+倍增)
题解 二分时间 然后一个显然的事是一个军队向上爬的越高它控制的点越多 所以首先军队尽量往上爬. 当一个军队可以爬到根节点我们记录下它的剩余时间T和它到达根结点时经过的根节点的子节点son. 当一个军队 ...
- luogu1084 [NOIp2012]疫情控制 (二分答案+倍增+dfs序)
先二分出一个时间,把每个军队倍增往上跳到不能再跳 然后如果它能到1号点,就记下来它跳到1号点后剩余的时间:如果不能,就让它就地扎根,记一记它覆盖了哪些叶节点(我在这里用了dfs序+差分,其实直接dfs ...
随机推荐
- Java - 无乱码读写文件
Java读取数据流的时候,一定要指定数据流的编码方式,否则将使用本地环境中的默认字符集. BufferedReader reader = null; String laststr = "&q ...
- 打造移动应用与游戏安全防线,腾讯WeTest安全服务全线升级
当移动互联网渗透到千家万户,与工业控制.智慧交通.实时社交.休闲娱乐紧密结合时,应用安全就变得尤为重要. 尤其在网络强相关的APP流行年代,当APP应用客户端上传与获取信息,大多通过接口在服务器双向通 ...
- ReadyAPI 教程和示例(二)
声明:如果你想转载,请标明本篇博客的链接,请多多尊重原创,谢谢! 本篇使用的 ReadyAPI版本是2.5.0 接上一篇: 4.修改SoapUI测试 本节将演示如何为测试用例添加测试步骤以及更改请求参 ...
- .NET MVC和.NET WEB api混用时注意事项
1.同时配置了mvc路由和api路由时,mvc路由无法访问(调用所有mvc路由全部404错误) 在Global.asax中,需注意路由注册的顺序,将api路由注册放在最后: 即将 void Appli ...
- Qt+opencv:读取、显示图像
GitHub:点击下载完整代码 本文主要是使用Qt与opencv将图像进行显示在QT界面上. 程序运行后的界面如下所示: (由于只有打开图像之后,才能对图像进行翻转,所以程序设置为读取图像成功之后才能 ...
- CodeForces 838B Diverging Directions 兼【20180808模拟测试】t3
描述 给你一个图,一共有 N 个点,2*N-2 条有向边. 边目录按两部分给出 1. 开始的 n-1 条边描述了一颗以 1 号点为根的生成树,即每个点都可以由 1 号点到达. 2. 接下来的 N-1 ...
- 一键部署pxe环境
系统:Centos6.5 环境:VMware Workstation12 #!/bin/bash # Please prepare CentOS ISO image first # root pass ...
- Machine Learning笔记整理 ------ (二)训练集与测试集的划分
在实际应用中,一般会选择将数据集划分为训练集(training set).验证集(validation set)和测试集(testing set).其中,训练集用于训练模型,验证集用于调参.算法选择等 ...
- 创新手机游戏《3L》开发点滴(3)——道具、物品、装备表设计 V2(最终版)
我们正在开发一款新手游,里面有道具,之前也写了一篇博文记录了下我们的设计思路,但是国庆到了,于是我有了时间继续纠结这个问题. 其实我主要是在到底是用mysql还是mongodb上纠结.这个复杂.痛苦. ...
- coding.net 版本控制
这是版本测试的所有内容,其中用到了 git 和coding的远程连接. 代码及版本控制 git地址:https://git.coding.net/tianjiping/11111.git