洛谷上的题目链接,题目不在赘述

题解

既然要时间最短,首先考虑二分。

因此,考虑二分时间,问题转换为如何检查能否到达。

如果一支军队一直向上走,能够到达根节点,那么他可以通过根节点到达其他的节点,因此这样的节点要单独拿出来匹配。

如果不能到达根节点,很显然,停在深度越低的位置,能够控制的子树越大,因此停留在深度最低的位置。

至于如何向上移动,显然不能够模拟,因此使用倍增计算向上移动。

因为有一些节点停在了中间,因此需要检查是否能够通过某些中间节点控制某棵子树。所以这里需要搜索一遍,检查哪些节点已经被那些停留的节点所控制。

接下来,剩下了一些东西:为被控制的根节点的儿子,和若干可以到达根节点的军队。

先在的问题就转换为了军队-节点的匹配。

很显然的一个贪心,到达根节点之后,如果一个军队剩余能够移动的时间越短,他就更加适合控制到根节点路径较短的节点相匹配。

但是这样还是有点小问题的。

因此,将贪心分点考虑如下:

①分别将子节点按照到根的路径长度,剩余军队按照剩余时间排序。

②显然当前军队要找到一个能够和自身匹配的军队为止(能够匹配的条件是:要么从这个子树上来的,要么剩余时间大于路径)

③在匹配过程中,如果一个节点因为剩余时间不够,无法用来匹配,则强行退回到上来的那棵子树

④如果一个军队匹配上了某一个节点,但是自己军队所上来的那个节点还没有被匹配,则优先退回去匹配。

这题真的很好,贪心一定要自己慢慢细想。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
using namespace std;
#define MAX 52000
#define INF 1000000000
struct Line
{
int v,next,w;
}e[MAX*2];
int cnt=1,h[MAX];
long long f[MAX][20];
int ned[MAX];
int dis[MAX],N,st[MAX],fr[MAX];
int d[MAX][20],M;
bool vis[MAX];
inline void Add(int u,int v,int w)
{
e[cnt]=(Line){v,h[u],w};
h[u]=cnt++;
}
inline int read()
{
int x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
void DFS(int u,int ff)
{
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==ff)continue;
f[v][0]=u;
d[v][0]=e[i].w;
dis[v]=dis[u]+e[i].w;
DFS(v,u);
}
}
inline void Pre()
{
DFS(1,0);
for(int j=1;j<=16;++j)
for(int i=1;i<=N;++i)
{
f[i][j]=f[f[i][j-1]][j-1];
d[i][j]=d[i][j-1]+d[f[i][j-1]][j-1];
}
}
inline bool cmp(int a,int b)
{
return dis[a]<dis[b];
}
inline int getc(int x)//计算当前节点在根节点的哪一个儿子节点的子树上
{
for(int i=16;i>=0;--i)
if(f[x][i]>1)x=f[x][i];
return x;
}
inline int Stop(int x,int t)//在t时间内能够到达的最大祖先位置
{
for(int i=16;i>=0;--i)
if(f[x][i]&&d[x][i]<=t)
{
t-=d[x][i];
x=f[x][i];
}
return x;
}
void DFS2(int x,int f)//检查控制
{
if(vis[x])return;
bool fl=true,dd=false;
for(int i=h[x];i;i=e[i].next)
{
int v=e[i].v;
if(v==f)continue;
dd=true;//检查是不是叶子节点
DFS2(v,x);
if(!vis[v])
fl=false;
}
vis[x]=fl&dd;
}
struct alive
{
int st,tt;
}ar[MAX];
bool operator <(alive a,alive b)
{
return a.tt<b.tt;
}
bool check(int t)
{
int cnt=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=M;++i)
{
int up=Stop(st[i],t);
if(up!=1)//不能够到达根节点
vis[Stop(st[i],t)]=true;//停在能够到达的最小深度
else
ar[++cnt]=(alive){fr[i],t-dis[st[i]]};
}
DFS2(1,0);
int cnt1=0,tot=0;
for(int i=h[1];i;i=e[i].next)//统计没有被控制的子树
if(!vis[e[i].v])
ned[++cnt1]=e[i].v;
sort(&ar[1],&ar[cnt+1]);//按照剩余时间排序
sort(&ned[1],&ned[cnt1+1],cmp);//按照距离排序
int now=1;tot=cnt;
for(int i=1;i<=cnt1;++i)//未被控制的子树一一匹配
{
if(vis[ned[i]])continue;
if(now==tot+1)return false;//当前的军队已经用完
while(233)
{
if(ar[now].tt<dis[ned[i]]&&now!=tot)
{
vis[ar[now].st]=true;//不能够解决别的地方,强行退回去
if(ned[i]==ar[now].st)break;
++now;//计算可以使用的时间剩余最少的军队
}
else
{
if(now==tot)break;
if(ar[now].st!=ned[i]&&(!vis[ar[now].st]))//虽然可以解决当前问题,但是自己所在子树未解决
{
vis[ar[now].st]=true;//强行退回去
++now;
}
else
break;
}
}
if(now==tot)
if(ar[now].tt<dis[ned[i]]&&ar[now].st!=ned[i])return false;
vis[ned[i]]=true;
now++;
}
return true;
}
int main()
{
N=read();
for(int i=1;i<N;++i)
{
int u=read(),v=read(),w=read();
Add(u,v,w);Add(v,u,w);
}
Pre();
M=read();
for(int i=1;i<=M;++i)st[i]=read();
for(int i=1;i<=M;++i)fr[i]=getc(st[i]);//计算每个儿子是从哪里来的
int l=0,r=INF;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d\n",l);
return 0;
}

【NOIP2012】疫情控制(二分,倍增,贪心)的更多相关文章

  1. NOIP2012疫情控制(二分答案+树上贪心)

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

  2. luogu1084 [NOIp2012]疫情控制 (二分答案+倍增+dfs序)

    先二分出一个时间,把每个军队倍增往上跳到不能再跳 然后如果它能到1号点,就记下来它跳到1号点后剩余的时间:如果不能,就让它就地扎根,记一记它覆盖了哪些叶节点(我在这里用了dfs序+差分,其实直接dfs ...

  3. Luogu P1084 疫情控制 | 二分答案 贪心

    题目链接 观察题目,答案明显具有单调性. 因为如果用$x$小时能够控制疫情,那么用$(x+1)$小时也一定能控制疫情. 由此想到二分答案,将问题转换为判断用$x$小时是否能控制疫情. 对于那些在$x$ ...

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

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

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

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

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

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

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

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

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

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

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

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

  10. Luogu1084 NOIP2012D2T3 疫情控制 二分答案、搜索、贪心、倍增

    题目传送门 题意太长就不给了 发现答案具有单调性(额外的时间不会对答案造成影响),故考虑二分答案. 贪心地想,在二分了一个时间之后,军队尽量往上走更好.所以我们预处理倍增数组,在二分时间之后通过倍增看 ...

随机推荐

  1. 读书共享 Primer Plus C-part 5

    第五章 运算符.表达式和语句 关于+- 的一元运算符和二元运算符的区别 a++:a先创建自身的一个副本,然后a自增1,最后返回副本的值 a+=1: 事实上相当于++a a=a+1: 虽然有点雷同于a+ ...

  2. HTML/CSS 常用单词整理

    页面布局(layout) header 头部/页眉: index 首页/索引: logo 标志: nav/sub_nav 导航/子导航: banner 横幅广告: main/content 主体/内容 ...

  3. Python自动化--语言基础4--模块、文件读写、异常

    模块1.什么是模块?可以理解为一个py文件其实就是一个模块.比如xiami.py就是一个模块,想引入使用就在代码里写import xiami即可2.模块首先从当前目录查询,如果没有再按path顺序逐一 ...

  4. CSS布局(四) float详解

    一.float设计初衷 因为float被设计出来的初衷是用于--文字环绕效果.即,一个图片一段文字,图片float:left之后,文字会环绕图片. <div style="width: ...

  5. FFMpeg在Ubuntu上的安装和使用

    在Ubuntu Server上编译FFmpeg FFmpeg是最流行的开源视频转码工具包,在Ubuntu上可以直接通过apt-get安装,但是默认的编码器不提供x264这些non-free的编码器,所 ...

  6. PHP开发中多种方案实现高并发下的抢购、秒杀功能

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...

  7. hdu1242 Rescue bfs+优先队列

    直接把Angle的位置作为起点,广度优先搜索即可,这题不是步数最少,而是time最少,就把以time作为衡量标准,加入优先队列,队首就是当前time最少的.遇到Angle的朋友就退出.只需15ms A ...

  8. c#判断外部可执行程序是否已打开(若未打开则打开)

    #region 通过当前代码执行路径向上找到相关exe,并根据processes.Length判断是否已启动 private bool CheckAndOpenExe(string exeName) ...

  9. 如何利用Plxmon工具进行在线烧录PCI卡的EEPROM

    PLXMON是PLX公司提供的用于在线烧录PCI卡的EEPROM的工具. 烧录EEPROM有两种方式,一是比较传统的方法,即采用烧录机进行烧录.采用这种方式时,在调试过程中EEPROM需采用插件式封装 ...

  10. 初次接触:DirectDraw

    第六章 初次接触:DirectDraw 本章,你将初次接触DirectX中最重要的组件:DirectDraw.DirectDraw可能是DirectX中最强大的技术,因为其贯穿着2D图形绘制同时其帧缓 ...