[NOIp2012]疫情控制 题解
好久没更,强迫自己写一篇。
神 tm 大预言家出的题
注意到如果 \(x\) 小时可以控制住疫情,则 \(\forall x'>x\) 必然也可以控制住疫情,显然答案具有单调性,可以二分答案。
考虑对于当前二分到的答案 \(mid\) 如何 check。根据贪心的策略,我们需要让所有军队的深度尽量小,于是可以考虑每一支军队向上跳的过程,这一步可以通过倍增预处理在 \(\log\) 时间实现。
对于在 \(mid\) 时间内无论如何也跳不到根的子节点的军队,就原地(指最后跳到的地方)驻扎,答案最优。
对于跳至根的子节点后仍有剩余时间的军队 \(s\),分成两种情况:
- 如果剩余时间不能使其在根节点之间跳一个往返,即剩余时间小于 \(2\times dis(s,root)\) 的军队,原地驻扎。
- 反之,将符合此条件的所有军队按照剩余时间排序,并将还未控制住疫情的根的子节点按照距根的距离排序,用双指针将军队与城市一一进行匹配。
考虑这么贪心做的正确性,对于剩余时间不够的军队,如果选择跳过树根去另一个子节点 \(s'\) 驻扎,则必然 \(dis(root,s)>dis(root,s')\),这么做可能会导致需要一个剩余时间足够的军队 \(s''\) 从其本来位置跨过根跳至 \(s\),花费时间 \(dis(root,s'')+dis(root,s)\),而这样做花的时间显然比 \(s\) 原地驻扎,\(s''\) 跨过根跳至 \(s'\) 的情况长,于是贪心策略正确。
当然,也有可能存在一个军队剩余时间不够,但是其子树已被另一个军队控制的情况,那么这个军队就应算入到上文的第二种情况中。
具体实现较为麻烦,可以根据个人编码习惯写,这里不想再赘述。
#include <bits/stdc++.h>
#define mem(a) memset(a,0,sizeof(a))
#define st first
#define nd second
using namespace std;
typedef long long ll;
const int N=5e4+5;
struct Edge{int to,nxt,w;}e[N<<1];
int head[N],n,m,cnt,fa[N][18],sod[N],dep[N];
ll dis[N],ans,mch[N],mch2[N];
bool fl,stay[N],mst[N];
pair<ll,int> h[N];
inline void add(int u,int v,int w) {e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;}
void dfs0(int u,int fat)
{
fa[u][0]=fat; dep[u]=dep[fat]+1;
for(int i=1;(1<<i)<=dep[u];++i)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=head[u],v;i;i=e[i].nxt)
{
v=e[i].to; if(v==fat) continue;
dis[v]=dis[u]+e[i].w;
dfs0(v,u);
}
}
bool dfs(int u)
{
if(stay[u]) return 1;
bool hasSon=0;
for(int i=head[u],v;i;i=e[i].nxt)
{
v=e[i].to; if(v==fa[u][0]) continue;
hasSon=1;
if(!dfs(v)) return 0;
}
return hasSon;
}
bool check(ll lim)
{
mem(stay); mem(mch);
mem(mch2); mem(h); mem(mst);
int toth=0,tota=0,totb=0;
for(int i=1;i<=m;++i)
{
ll sum=0; int x=sod[i];
for(int j=log2(dep[x]);~j;--j)
if(fa[x][j]>1&&sum+dis[x]-dis[fa[x][j]]<=lim)
sum+=dis[x]-dis[fa[x][j]],x=fa[x][j];
if(fa[x][0]==1&&sum+dis[x]<=lim) h[++toth]={lim-sum-dis[x],x};
else stay[x]=1;
}
for(int i=head[1];i;i=e[i].nxt)
if(!dfs(e[i].to)) mst[e[i].to]=1;
for(int i=1;i<=toth;++i)
if(mst[h[i].nd]&&h[i].st<dis[h[i].nd]) mst[h[i].nd]=0;
else mch[++tota]=h[i].st;
for(int i=head[1];i;i=e[i].nxt)
if(mst[e[i].to]) mch2[++totb]=dis[e[i].to];
if(tota<totb) return 0;
sort(mch+1,mch+tota+1);
sort(mch2+1,mch2+totb+1);
int i=1,j=1;
while(i<=tota&&j<=totb)
if(mch[i]>=mch2[j]) ++i,++j;
else ++i;
return j>totb;
}
int main()
{
scanf("%d",&n); ll sum=0;
for(int i=1,a,b,c;i<n;++i)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c); add(b,a,c);
sum+=c;
}
dfs0(1,0);
scanf("%d",&m);
for(int i=1;i<=m;++i) scanf("%d",&sod[i]);
ll l=0,r=sum;
while(l<=r)
{
ll mid=l+r>>1;
if(check(mid)) ans=mid,r=mid-1,fl=1;
else l=mid+1;
}
printf("%lld",fl?ans:-1);
return 0;
}
[NOIp2012]疫情控制 题解的更多相关文章
- NOIP2012 疫情控制 题解(LuoguP1084)
NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...
- noip2012疫情控制 题解
题目大意 给出一棵n个节点的树,根是1,要在除根节点以外的点建立检查点,使得从每条根到叶子的路径上都至少存在一个检查点.检查点由军队来建立.初始军队的位置是给定的,移动军队走一条边需要花费这条边的权值 ...
- [NOIP2012]疫情控制 贪心 二分
题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...
- luoguP1084 疫情控制(题解)(搜索+贪心)
luoguP1084 疫情控制 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include& ...
- NOIP2012 D2T3 疫情控制 题解
题面 这道题由于问最大值最小,所以很容易想到二分,但怎么验证并且如何实现是这道题的难点: 首先我们考虑,对于一个军队,尽可能的往根节点走(但一定不到)是最优的: 判断一个军队最远走到哪可以树上倍增来实 ...
- noip2012 疫情控制
[问题描述] H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子 ...
- NOIP2012疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- [NOIP2012]疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
随机推荐
- 菜鸟刷题路:剑指 Offer 05. 替换空格
剑指 Offer 05. 替换空格 class Solution { public String replaceSpace(String s) { StringBuilder str = new St ...
- springboot实现自定义拦截器
为了更容易理解,我们通过一个代码例子来演示. 例子: 我们现在要访问http://localhost:8080/main.html页面,这个页面需要登录之后才能够浏览,没登录不能浏览. 那么现在问题来 ...
- 技能篇:awk教程-linux命令
前言 AWK是一门解释型的编程语言.用于文本处理,它的名字来源于它的三位作者的姓氏:Alfred Aho, Peter Weinberger 和 Brian Kernighan awk 程序结构 运行 ...
- Java IO学习笔记五:BIO到NIO
作者:Grey 原文地址: Java IO学习笔记五:BIO到NIO 准备环境 准备一个CentOS7的Linux实例: 实例的IP: 192.168.205.138 我们这次实验的目的就是直观感受一 ...
- 终于放弃了单调的swagger-ui了,选择了这款神器—knife4j
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案(在非Java项目中也提供了前端UI的增强解决方案),前身是swagger-bootstrap-ui,取名knife ...
- MindSpore自定义模型损失函数
技术背景 损失函数是机器学习中直接决定训练结果好坏的一个模块,该函数用于定义计算出来的结果或者是神经网络给出的推测结论与正确结果的偏差程度,偏差的越多,就表明对应的参数越差.而损失函数的另一个重要性在 ...
- 冷饭新炒 | 深入Quartz核心运行机制
目录 Quartz的核心组件 JobDetail Trigger 为什么JobDetail和Trigger是一对多的关系 常见的Tigger类型 怎么排除掉一些日期不触发 Scheduler List ...
- 安装linux 报错(initramfs) Unable to find a medium containing a live file system
如题,linux 安装报错:(initramfs) Unable to find a medium containing a live file system 我是用UItraISO做的启动盘遇到这个 ...
- Dockerfile优化方式
如今GitHub 仓库中已经包含了成千上万的Dockerfile,但并不是所有的Dockerfile都是高效的.本文将从四个方面来介绍Dockerfile的最佳实践,以此来帮助大家编写更优雅的Dock ...
- Redis的bitmap从基础到业务
1. 位与字节 1个字节(byte)等于8个位(bit).(计算机常识). 2. string与bitmap Redis里的bitmap是属于string这个数据类型里的.可以help进行查看bit相 ...