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

题解

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#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. 关于git post-receive 钩子

    登录 git服务器 进入你项目所在git文件夹 cd /var/opt/gitlab/git-data/repositories/liangyuquan/yfg.git cd hooks vim po ...

  2. css命名整理

    .container { width: 720px; background: #fafafa; border: 2px dashed #999; padding: 10px; float: left ...

  3. 原生Java代码拷贝目录

    拷贝.移动文件(夹),有三方包commons-io可以用,但是有时候有自己的需求,只能使用原生java代码,这时可以用以下几种方式进行拷贝: 1.使用系统命令(Linux)调用 此种方式对操作系统有要 ...

  4. 彻底理解 Android 中的阴影

    如果我们想创造更好的 Android App,我相信我们需要遵循 Material Design 的设计规范.一般而言,Material Design 是一个包含光线,材质和投影的三维环境.如果我们想 ...

  5. HDU 6035(树形dp)

    题意略. 思路:有n * (n - 1) / 2这么多边,要枚举是不可能的,感觉和数据结构也沾不上边.再加上树上染色,以一条边上不同颜色作为这个边的值,这看起来像是算贡献那种题,和17icpc沈阳的某 ...

  6. idea 使用debugger技巧

    1,背景 每个开发人员每天都离不开debugger,只要你在编码,就需要调试,作为一个开发快10年的老程序员每天都要写很多代码,当每个人接到任务的时候都会想,这些功能其实很快就能写完,没错,对于写代码 ...

  7. 深度学习优化算法Momentum RMSprop Adam

    一.Momentum 1. 计算dw.db. 2. 定义v_db.v_dw \[ v_{dw}=\beta v_{dw}+(1-\beta)dw \] \[ v_{db}=\beta v_{db}+( ...

  8. 用pycharm+flask 建立项目以后运行出现ImportError: No module named flask-login问题

    出现此问题,一般情况下: 打开CMD输入: pip install flask-login 然后,在cmd中输入命令: pip list 查看目前已安装的的模板.在此时,如果你继续运行项目,有可能会发 ...

  9. Spring MVC处理(下周完善)

    http://www.cnblogs.com/xiepeixing/p/4244574.html http://blog.csdn.net/kobejayandy/article/details/12 ...

  10. JAVA多线程与并发学习总结

    1.      计算机系统 使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行:当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了. 缓 ...