【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 疫情控制 二分答案、搜索、贪心、倍增
题目传送门 题意太长就不给了 发现答案具有单调性(额外的时间不会对答案造成影响),故考虑二分答案. 贪心地想,在二分了一个时间之后,军队尽量往上走更好.所以我们预处理倍增数组,在二分时间之后通过倍增看 ...
随机推荐
- php+redis 学习 二 悲观锁
<?php header('content-type:text/html;chaeset=utf-8'); /** * redis实战 * * 实现悲观锁机制 * */ $timeout = 5 ...
- OpenCV角点检测源代码分析(Harris和ShiTomasi角点)
OpenCV中常用的角点检测为Harris角点和ShiTomasi角点. 以OpenCV源代码文件 .\opencv\sources\samples\cpp\tutorial_code\Trackin ...
- 损失函数 hinge loss vs softmax loss
1. 损失函数 损失函数(Loss function)是用来估量你模型的预测值 f(x) 与真实值 Y 的不一致程度,它是一个非负实值函数,通常用 L(Y,f(x)) 来表示. 损失函数越小,模型的鲁 ...
- jq模仿雨滴下落的动画
效果如图: 实现思路:定时器每隔x秒生成宽高.下落速度(即动画执行时间).left随机的div. 1.CSS: body{ overflow: hidden;/*这是为了防止出现滚动条*/ } .co ...
- Failure to transfer org.apache.maven:maven-archiver:pom:2.5 from http://repo.maven.apache.org/ maven2 was cached in the local repository, resolution will not be reattempted until the update interv
Failure to transfer org.apache.maven:maven-archiver:pom:2.5 from http://repo.maven.apache.org/ mave ...
- GET和POST传送数据的限制
GET 方法 请注意,查询字符串(名称/值对)是在 GET 请求的 URL 中发送的: /test/demo_form.asp?name1=value1&name2=value2 有关 GET ...
- 《android开发艺术探索》读书笔记(十五)--Android性能优化
接上篇<android开发艺术探索>读书笔记(十四)--JNI和NDK编程 No1: 如果<include>制定了这个id属性,同时被包含的布局文件的根元素也制定了id属性,那 ...
- uva437 DAG
直接套用DAG的思路就行. AC代码: #include<cstdio> #include<cstring> #include<algorithm> using n ...
- POJ - 3126 bfs + 素数筛法 [kuangbin带你飞]专题一
题意:给定两个四位素数作为终点和起点,每次可以改变起点数的某一位,且改变后的数仍然是素数,问是否可能变换成终点数字? 思路:bfs搜索,每次改变四位数中的某一位.素数打表方便判断新生成的数是否是素数. ...
- C 语言中模板的几种实现方式
简单宏定义实现 简单宏定义 - 方式一 这种方式将主要实现部分放在一个宏定义中,利用字符替换的方式实现不同 type 的运算,详细思路见代码: simple_macro_1.c #include &l ...