[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国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
随机推荐
- 【NX二次开发】镜像对象
使用uf5946获取镜像矩阵注意:uf5946镜像这个函数,只能用#define UF_plane_type=46这种类型的数据作为镜像面,不能用#define UF_datum_plane_type ...
- 【VBA】单元格插入图片,单元格删除图片
封装函数: Sub 插入产品形象(strRange As String, datebaseTu As String) Dim strJpg As String strJpg = datebaseTu ...
- 05:JS(01)
内容概要 JavaScript编程语言开头 数值类型 字符类型 布尔值 null与undefined 对象 数组 自定义对象 流程控制 函数 内置对象 时间对象 正则对象 JSON对象 BOM操作(j ...
- C# 24点游戏求解算法
经常跟儿子玩24点,有时候比较难算的,算一会儿,两人算不出来,就收了,当作没法算. 以我的数学能力,一般来说,算不出来的,大概率确实是算不出来的. 但是遇到比较变态的,当作算不出来是可能的,所以一直想 ...
- [翻译]Go与C#的比较,第二篇:垃圾回收
Go vs C#, part 2: Garbage Collection | by Alex Yakunin | ServiceTitan - Titan Tech | Medium 目录 译者注 什 ...
- 我试了试用 SQL查 Linux日志,好用到飞起
大家好,我是小富~ 最近发现点好玩的工具,迫不及待的想跟大家分享一下. 大家平时都怎么查Linux日志呢? 像我平时会用tail.head.cat.sed.more.less这些经典系统命令,或者aw ...
- redis cluster如何支持pipeline
当我们要操作一批key时,可以通过 redis pipline 再执行完后一次性读取所有结果来较少网络传输的消耗: 很明显,这有个限制条件 => 这批key的执行必须在同一个连接上 当部署的re ...
- Vue指令实现原理
前言 自定义指令是vue中使用频率仅次于组件,其包含bind.inserted.update.componentUpdated.unbind五个生命周期钩子.本文将对vue指令的工作原理进行相应介绍, ...
- 把 STM32 bluepill 变成调试器(daplink)
在调一块 ARM M0 内核的板子,使用官方的 DEMO 板子来调,板子上集成了 daplink 调试器. 为了方便使用,我把目标板跟 daplink 剪开了,然后用杜邦线把 daplink 跟目标板 ...
- 『动善时』JMeter基础 — 50、使用JMeter测试WebService接口
目录 1.什么是WebService 2.WebService和SOAP的关系 3.什么是WSDL 4.测试WebService接口前的准备 (1)如何判断是WebService接口 (2)如何获取W ...