疫情控制
(blockade.cpp/c/pas)
【问题描述】
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
【输入】
输入文件名为 blockade.in。
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
【输出】
输出文件为 blockade.out。
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 10;
对于 40%的数据,2 ≤n≤50,0<w <10 5 ;
对于 60%的数据,2 ≤ n≤1000,0<w <10 6 ;
对于 80%的数据,2 ≤ n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w <10 9 。

正解:贪心+二分答案+倍增

解题报告:

  写每年NOIP的T3找感觉。。。

  这道题开始觉得很码农,打完之后发现也就那样。。。网上题解讲的并不是很清楚,我详细讲一讲吧。

  这题还是运用一些比较神奇的东西。题意求一个最大值的最小值,我居然第一眼没看出来二分答案,显然直接二分一个时间,然后判断是否可行。

  考虑如何判断可行,得到一个时间之后,我们就可以知道所有军队是否能到达1号(首都),也就是根结点,为了很快求出来我们需要预处理一下距离,倍增自然最好。我们不妨对于每一支军队都算一下是否可以到达根结点,不能的话就记录一下最多能跳到哪里。对于这道题,我们可以很明显地看出越往上越优,所以我们对于不能到达根的就尽可能地往上跳,到他能达到的最高结点,打上标记,表示这个结点可以到达。然后我们就dfs一遍,自下往上判断一下,如果一个结点的所有儿子都已经存在封锁到叶子结点的距离了,那么这个结点也意味着封锁了,往上update就可以了。

  接下来我们统计一下根结点的所有儿子结点,那些已经封锁边境的我们肯定不需要再管了,已经可以覆盖了。我们只要考虑尚未封锁的,显然对于每个儿子结点,若想封闭这个儿子结点到边境,最优的肯定是直接封锁这个儿子结点。所以我们可以考虑对于那些可以到达根结点的军队,把他们派往各个未被封锁的儿子结点。当然,如果可派出的军队数如果还没有未封锁的儿子结点数量多,那么无论如何不可行。

  我们现在就有一些军队到达根结点之后剩余的行动时间和每个未被封锁的儿子结点到根结点的距离。我们需要将他们进行匹配,也就是把军队派往各地。首先,我们必须明确,如果某只军队可以到达根结点,而他所在的根结点的儿子结点的那棵树中,不妨设为root,肯定把他直接派往root驻扎即可,因为把他派往别的未被封锁的城市的话,还需要另外调军队过来驻扎这个root,肯定不会更优,具体证明略。

  综上,我们的判断做法就是:军队按剩余时间排序,未被封锁的儿子结点按到根结点距离排序,而每支军队如果他的root未被封锁,则直接派往root,否则选择一个距离最近的未被封锁的儿子结点,前往驻扎,因为如果最近的都无法派往的话,这支军队就不能发挥任何作用了。(这是一个经典的O(N)匹配。。。)

  就这么做啦,代码如下:

 //It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long LL;
const int MAXN = ;
LL inf;
int n,m,ecnt,a[MAXN];
int root[MAXN];//属于根结点的哪个子结点
int next[MAXN*],to[MAXN*],first[MAXN],w[MAXN*];
int f[MAXN][];
LL g[MAXN][];
LL l,r,ans,mid;
bool pd[MAXN];
struct node{
LL remain;
int root;
}b[MAXN];
struct son{
LL dis;
int pos;
}c[MAXN]; inline int getint()
{
int w=,q=; char c=getchar();
while((c<'' || c>'') && c!='-') c=getchar(); if(c=='-') q=,c=getchar();
while (c>='' && c<='') w=w*+c-'', c=getchar(); return q ? -w : w;
} inline bool cmp(node q,node qq){ return q.remain<qq.remain; }
inline bool ccmp(son q,son qq){ return q.dis<qq.dis; } inline void dfs(int x,int fa){
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
if(x==) root[v]=v; else root[v]=root[x];
dfs(v,x); f[v][]=x; g[v][]=w[i];
}
} inline void dfs2(int x,int fa){
bool flag=false,ff=true;
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
flag=true; dfs2(v,x);
if(!pd[v]) ff=false;
}
if(ff && flag && x!=) pd[x]=;
} inline bool check(LL limit){
int u; LL total;
int cnt=; memset(pd,,sizeof(pd));
for(int i=;i<=m;i++) {
u=a[i]; total=limit;
for(int j=;j>=;j--) {
if(total>=g[u][j] && f[u][j]!=) {
total-=g[u][j];
u=f[u][j];
}
}
if(u==) {
b[++cnt].remain=total;
b[cnt].root=root[a[i]];
}
else pd[u]=;
} dfs2(,);//处理跳到一半到达不了根的情况,往上update,注意顺序!!! ecnt=;//统计根结点的未被覆盖的儿子个数
for(int i=first[];i;i=next[i]) {
if(pd[to[i]]) continue;//统计未被覆盖的!!!
c[++ecnt].pos=to[i];
c[ecnt].dis=w[i];
} if(ecnt>cnt) return false;
sort(b+,b+cnt+,cmp); sort(c+,c+ecnt+,ccmp);
int now=;//当前处理到的根结点的儿子结点
c[ecnt+].dis=inf;
for(int i=;i<=cnt;i++) {
if(!pd[b[i].root]) pd[b[i].root]=; //当前军队处在这个儿子结点尚未被覆盖,那么直接派遣他过去驻扎就可以了
else {
while(pd[c[now].pos]) now++;
if(b[i].remain>=c[now].dis) pd[c[now].pos]=,now++;
}
while(pd[c[now].pos]) now++;
}
if(now>ecnt) return true;
return false;
} inline void work(){
n=getint(); int x,y,z;
for(int i=;i<n;i++) {
x=getint(); y=getint(); z=getint();
next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
}
m=getint(); for(int i=;i<=m;i++) a[i]=getint();
dfs(,);
for(int j=;j<=;j++)//注意倍增细节,更新顺序!!!
for(int i=;i<=n;i++){
f[i][j]=f[f[i][j-]][j-];
g[i][j]=g[f[i][j-]][j-]+g[i][j-];
}
inf=; for(int i=;i<=;i++) inf*=;
l=; r=inf;
if(!check(r)) { printf("-1"); return ; }
while(l<=r) {
mid=(l+r)/;
if(check(mid)) ans=mid,r=mid-;
else l=mid+;
}
printf("%lld",ans);
} int main()
{
work();
return ;
}

codevs1218 疫情控制的更多相关文章

  1. Codevs 1218 疫情控制 2012年NOIP全国联赛提高组

    1218 疫情控制 2012年NOIP全国联赛提高组 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description H 国有 n 个城市,这 ...

  2. 【NOIP2012】 疫情控制

    [NOIP2012] 疫情控制 标签: 倍增 贪心 二分答案 NOIP Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...

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

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

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

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

  5. 疫情控制 blockade

    疫情控制 blockad 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当 ...

  6. 疫情控制 2012年NOIP全国联赛提高组(二分答案+贪心)

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

  7. CH6301 疫情控制

    6301 疫情控制 0x60「图论」例题 描述 H国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病. ...

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

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

  9. 洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

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

随机推荐

  1. 千万级的大表!MySQL这样优化更好

    对于一个千万级的大表,现在可能更多的是亿级数据量,很多人第一反应是各种切分,可结果总是事半功倍,或许正是我们优化顺序的不正确.下面我们来谈谈怎样的优化顺序可以让效果更好. MySQL数据库一般都是按照 ...

  2. Linux个人常用命令汇总

    1. 查询端口状态 netstat -apn | grep [port] 得到pid=64472 2. 根据进程id查询程序信息 ps -aux | grep [pid] 3. CentOS防火墙 参 ...

  3. httpd在嵌入式中应用

    在启动脚本合适位置添加: httpd -h /usr/app/www/ 即开始httpd服务,并定位到/usr/app/www/ 注:busybox已支持httpd命令,所以直接用即可. busybo ...

  4. 使用phpize建立php扩展(Cannot find config.m4)

    php源码:/root/soft/php-5.3.4php安装: /usr/local/php [root@ns root]# phpizeCannot find config.m4.Make sur ...

  5. SQLSERVER---- 通过位运算更改标志位

    当给多个中心传输数据时,怎么标记哪些单位推送了,哪些单位没有更新,如果单独设置一个字段,一来说,扩展不足,另外会造成数据库冗余,这里可以采用SQLSERVER的位运算. 比如说,更新标志位为0,长度为 ...

  6. OpenCv for Android 环境搭建

    最近工作需要这样的功能 如下图 要在类似功能在android上实现 然后实现成这样 这两张图来自博客:图像校正—透视变换 可惜他用的是C/C++语言写的调用opencv,我参考了下他写的方案就想到了a ...

  7. atom常用插件安装

    安装插件方法: File -Settings -Install 在搜索框里搜索你想要的插件,出来之后 点击install ,下图以 linter-selint 为例 ATOM常用插件推荐 simpli ...

  8. html5小趣味知识点系列(一)required

    都知道这个属性是检查你 是否填写了字段也就是说咱们不用判断输入的数值是否为空的情况了 但是这个属性一定要和form配合在一起使用单独的使用是不可以实现的 <!DOCTYPE html> & ...

  9. centos7.0 安装redis 3.2.9

    wget http://download.redis.io/releases/redis-3.2.9.tar.gz tar xzf redis-3.2.9.tar.gz cd redis-3.2.9 ...

  10. 【BZOJ3302】[Shoi2005]树的双中心 DFS

    [BZOJ3302][Shoi2005]树的双中心 Description Input 第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号.接下来N-1行,每行两个整 ...