【NOIP2012】疫情控制(二分,倍增,贪心)
题解
既然要时间最短,首先考虑二分。
因此,考虑二分时间,问题转换为如何检查能否到达。
如果一支军队一直向上走,能够到达根节点,那么他可以通过根节点到达其他的节点,因此这样的节点要单独拿出来匹配。
如果不能到达根节点,很显然,停在深度越低的位置,能够控制的子树越大,因此停留在深度最低的位置。
至于如何向上移动,显然不能够模拟,因此使用倍增计算向上移动。
因为有一些节点停在了中间,因此需要检查是否能够通过某些中间节点控制某棵子树。所以这里需要搜索一遍,检查哪些节点已经被那些停留的节点所控制。
接下来,剩下了一些东西:为被控制的根节点的儿子,和若干可以到达根节点的军队。
先在的问题就转换为了军队-节点的匹配。
很显然的一个贪心,到达根节点之后,如果一个军队剩余能够移动的时间越短,他就更加适合控制到根节点路径较短的节点相匹配。
但是这样还是有点小问题的。
因此,将贪心分点考虑如下:
①分别将子节点按照到根的路径长度,剩余军队按照剩余时间排序。
②显然当前军队要找到一个能够和自身匹配的军队为止(能够匹配的条件是:要么从这个子树上来的,要么剩余时间大于路径)
③在匹配过程中,如果一个节点因为剩余时间不够,无法用来匹配,则强行退回到上来的那棵子树
④如果一个军队匹配上了某一个节点,但是自己军队所上来的那个节点还没有被匹配,则优先退回去匹配。
这题真的很好,贪心一定要自己慢慢细想。
#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ⅆ
}
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】疫情控制(二分,倍增,贪心)的更多相关文章
- NOIP2012疫情控制(二分答案+树上贪心)
H 国有n个城市,这 n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示 ...
- luogu1084 [NOIp2012]疫情控制 (二分答案+倍增+dfs序)
先二分出一个时间,把每个军队倍增往上跳到不能再跳 然后如果它能到1号点,就记下来它跳到1号点后剩余的时间:如果不能,就让它就地扎根,记一记它覆盖了哪些叶节点(我在这里用了dfs序+差分,其实直接dfs ...
- Luogu P1084 疫情控制 | 二分答案 贪心
题目链接 观察题目,答案明显具有单调性. 因为如果用$x$小时能够控制疫情,那么用$(x+1)$小时也一定能控制疫情. 由此想到二分答案,将问题转换为判断用$x$小时是否能控制疫情. 对于那些在$x$ ...
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...
- [NOIP2012]疫情控制 贪心 二分
题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...
- NOIP2012 疫情控制 题解(LuoguP1084)
NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...
- luoguP1084 疫情控制(题解)(搜索+贪心)
luoguP1084 疫情控制 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include& ...
- NOIP2012疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- [NOIP2012]疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- Luogu1084 NOIP2012D2T3 疫情控制 二分答案、搜索、贪心、倍增
题目传送门 题意太长就不给了 发现答案具有单调性(额外的时间不会对答案造成影响),故考虑二分答案. 贪心地想,在二分了一个时间之后,军队尽量往上走更好.所以我们预处理倍增数组,在二分时间之后通过倍增看 ...
随机推荐
- 好用的Google漏洞爬虫:Google Mass Explorer
这是一款基于谷歌搜索引擎的自动化爬虫. 爬虫介绍 爬虫大体机制就是: 先进行一次谷歌搜索,将结果解析为特定格式,然后再提供给exp使用. 大家可以尝试使用–help来列出所有参数. 这个项目笔者会持续 ...
- Maven中避开测试环节
两种方法 修改pom文件 添加<skipTests>true</skipTests>标签 <plugin> <groupId>org.apache.ma ...
- python配置apache的web服务器方法(python的CGI配置)
先大概介绍一下:Python CGI编程 什么是CGI CGI 目前由NCSA维护,NCSA定义CGI如下: CGI(Common Gateway Interface),通用网关接口,它是一段程序,运 ...
- Go语言极速入门手册
Github: https://github.com/coderzh/CodeTips /* gotips_test.go: Golang速学速查速用代码手册 Source: github.com/c ...
- Redis 实践3-操作
string常用操作 set key1 aminglinux get key1 set key1 aming //一个key对应一个value,多次赋值,会覆盖前面的value setnx k ...
- SpringBoot 中常用注解
本篇博文将介绍几种SpringBoot 中常用注解 其中,各注解的作用为: @PathVaribale 获取url中的数据 @RequestParam 获取请求参数的值 @GetMapping 组合注 ...
- Opencv 3.3.0 常用函数
如何调图像的亮度和对比度? //如何增加图片的对比度或亮度? void contrastOrBrightAdjust(InputArray &src,OutputArray &dst, ...
- 微软Surface Book推送Windows 10新固件更新:增强系统和电池
微软公司最近为Surface Book推出了新的Windows 10固件系统和驱动的更新,并且以MSI的文件格式上传到了微软的下载中心里面.此次更新的内容主要是改进了Surface Book的系统的稳 ...
- eclipse Maven配置
①下载:http://maven.apache.org/download.cgi ②解压至:F:\Study\apache-maven-3.5.2 ③配置环境变量 变量名:M2_HOME 变量值:F: ...
- (2018干货系列五)最新UI设计学习路线整合
怎么学UI全链路设计 全链路设计师是参与整个商业链条,为每个会影响用户体验的地方提供设计的可解决方案,最后既满足了商业目标,又提升了产品的用户体验和设计质量,与平面设计.UI设计彻底区分开来,是真正的 ...