好久没更,强迫自己写一篇。

神 tm 大预言家出的题


注意到如果 \(x\) 小时可以控制住疫情,则 \(\forall x'>x\) 必然也可以控制住疫情,显然答案具有单调性,可以二分答案。

考虑对于当前二分到的答案 \(mid\) 如何 check。根据贪心的策略,我们需要让所有军队的深度尽量小,于是可以考虑每一支军队向上跳的过程,这一步可以通过倍增预处理在 \(\log\) 时间实现。

对于在 \(mid\) 时间内无论如何也跳不到根的子节点的军队,就原地(指最后跳到的地方)驻扎,答案最优。

对于跳至根的子节点后仍有剩余时间的军队 \(s\),分成两种情况:

  1. 如果剩余时间不能使其在根节点之间跳一个往返,即剩余时间小于 \(2\times dis(s,root)\) 的军队,原地驻扎。
  2. 反之,将符合此条件的所有军队按照剩余时间排序,并将还未控制住疫情的根的子节点按照距根的距离排序,用双指针将军队与城市一一进行匹配。

考虑这么贪心做的正确性,对于剩余时间不够的军队,如果选择跳过树根去另一个子节点 \(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]疫情控制 题解的更多相关文章

  1. NOIP2012 疫情控制 题解(LuoguP1084)

    NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...

  2. noip2012疫情控制 题解

    题目大意 给出一棵n个节点的树,根是1,要在除根节点以外的点建立检查点,使得从每条根到叶子的路径上都至少存在一个检查点.检查点由军队来建立.初始军队的位置是给定的,移动军队走一条边需要花费这条边的权值 ...

  3. [NOIP2012]疫情控制 贪心 二分

    题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...

  4. Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)

    Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...

  5. luoguP1084 疫情控制(题解)(搜索+贪心)

    luoguP1084 疫情控制 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include& ...

  6. NOIP2012 D2T3 疫情控制 题解

    题面 这道题由于问最大值最小,所以很容易想到二分,但怎么验证并且如何实现是这道题的难点: 首先我们考虑,对于一个军队,尽可能的往根节点走(但一定不到)是最优的: 判断一个军队最远走到哪可以树上倍增来实 ...

  7. noip2012 疫情控制

    [问题描述] H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子 ...

  8. NOIP2012疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

  9. [NOIP2012]疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

随机推荐

  1. 【NX二次开发】镜像对象

    使用uf5946获取镜像矩阵注意:uf5946镜像这个函数,只能用#define UF_plane_type=46这种类型的数据作为镜像面,不能用#define UF_datum_plane_type ...

  2. 【VBA】单元格插入图片,单元格删除图片

    封装函数: Sub 插入产品形象(strRange As String, datebaseTu As String) Dim strJpg As String strJpg = datebaseTu ...

  3. 05:JS(01)

    内容概要 JavaScript编程语言开头 数值类型 字符类型 布尔值 null与undefined 对象 数组 自定义对象 流程控制 函数 内置对象 时间对象 正则对象 JSON对象 BOM操作(j ...

  4. C# 24点游戏求解算法

    经常跟儿子玩24点,有时候比较难算的,算一会儿,两人算不出来,就收了,当作没法算. 以我的数学能力,一般来说,算不出来的,大概率确实是算不出来的. 但是遇到比较变态的,当作算不出来是可能的,所以一直想 ...

  5. [翻译]Go与C#的比较,第二篇:垃圾回收

    Go vs C#, part 2: Garbage Collection | by Alex Yakunin | ServiceTitan - Titan Tech | Medium 目录 译者注 什 ...

  6. 我试了试用 SQL查 Linux日志,好用到飞起

    大家好,我是小富~ 最近发现点好玩的工具,迫不及待的想跟大家分享一下. 大家平时都怎么查Linux日志呢? 像我平时会用tail.head.cat.sed.more.less这些经典系统命令,或者aw ...

  7. redis cluster如何支持pipeline

    当我们要操作一批key时,可以通过 redis pipline 再执行完后一次性读取所有结果来较少网络传输的消耗: 很明显,这有个限制条件 => 这批key的执行必须在同一个连接上 当部署的re ...

  8. Vue指令实现原理

    前言 自定义指令是vue中使用频率仅次于组件,其包含bind.inserted.update.componentUpdated.unbind五个生命周期钩子.本文将对vue指令的工作原理进行相应介绍, ...

  9. 把 STM32 bluepill 变成调试器(daplink)

    在调一块 ARM M0 内核的板子,使用官方的 DEMO 板子来调,板子上集成了 daplink 调试器. 为了方便使用,我把目标板跟 daplink 剪开了,然后用杜邦线把 daplink 跟目标板 ...

  10. 『动善时』JMeter基础 — 50、使用JMeter测试WebService接口

    目录 1.什么是WebService 2.WebService和SOAP的关系 3.什么是WSDL 4.测试WebService接口前的准备 (1)如何判断是WebService接口 (2)如何获取W ...