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

题解

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#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. xBIM WeXplorer xViewer 浏览器检查

    目录 基础 xBIM WeXplorer 简要介绍 xBIM WeXplorer xViewer 基本应用 xBIM WeXplorer xViewer 浏览器检查 xBIM WeXplorer xV ...

  2. nginx/php-fpm 访问php文件直接下载而不运行

    遇到这种问题,首先确认你web服务器配置中的.PHP是不是被指定给FastCGI server处理: location ~ .php$ { fastcgi_pass ; } 如已配置,那么可能是由于f ...

  3. kvm克隆

    virt-clone --original aming2 --name aming3 --file /data/kvm/aming3.qcow2   相关的克隆命令 克隆前必须关闭虚拟机   virs ...

  4. Docker Centos6 下建立 Docker 桥接网络

    cd /etc/sysconfig/network-scripts/; cp ifcfg-eth0  ifcfg-br0 vi ifcfg-eth0 //增加BRIDGE=br0,删除IPADDR,N ...

  5. 一步一步从原理跟我学邮件收取及发送 13.mime格式与常见字符编码

    在前面的本系列文章中我们已经学会了邮件的发送和收取.但在收取中我们看到的是一串串的乱码,回忆前面的发送过程,我们会奇怪:我们前面的邮件是明文啊.为什么明文的邮件明明也可以正常工作,还要弄乱码似的字符串 ...

  6. java中try catch块的使用

    对于关流操作的时候,最好采用如下语句块: InputStream in=......; try{ try{ //some statemenet }finally{ //close stream in. ...

  7. 深入js正则

    开题 我们常常有正则的各种需求,普通的正则匹配符虽然够用,但是满足不了我们一些很奇怪的需求,所以我们需要更多的关于正则的知识点. 需求 比如我们有一个这样的需求,匹配出字符串里的第一个div标签:aa ...

  8. 让网站通过Https访问

    Prerequisites Before you begin, you should have some configuration already taken care of. We will be ...

  9. github的拉取、提交,创建分支与合并

    前期准备: 1.安装git  官网地址:https://git-scm.com/(下载下来,直接下一步)   2.github账号(这有点废话)   3.配置github密钥 下载及安装好git后,右 ...

  10. JMeter使用(Linux)

    JMeter是一个Java桌面应用程序,用户界面采用Swing Java Api实现,支持并发和多线程或者线程组的执行,对于配置负载测试和压力测试非常有用.Jmeter是开源.免安装的,只需要有jdk ...