疫情控制
(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. java编写socket使用bufferedReader.readLine()问题研究

    不写java代码好久,临时写个socket通讯竟然失败,郁闷之下仔细研究了下. 客户端使用BufferedReader来读取数据,在while中调用BufferedReader.readLine()函 ...

  2. lua学习笔记(四)

      表达式   算术操作符     +(加法) -(减法) *(乘法) /(除法) ^(指数) %(取模) -(负号)     x%1的结果是x的小数部分,x-x%1是整数部分   关系操作符     ...

  3. 前台freemark获取后台的值

    1.后台代码: ModelAndView mv = new ModelAndView("log/logList.ftl"); String info="abc" ...

  4. 解决Java工程URL路径中含有中文的情况

    问题: 当Java工程路径中含有中文时,得不到正确的路径 *** 解决: 这其实是编码转换的问题.当我们使用ClassLoader的getResource方法获取路径时,获取到的路径被URLEncod ...

  5. 安装mingw后,在命令窗体编译c文件

    1.编译test.cpp文件 #include<iostream> int main(int argc,char **argv) {   std::cout<<"he ...

  6. sudo npm install -g cnpm --registry=https://registry.npm.taobao.org

  7. hdu_1226超级密码(BFS)

    超级密码 Problem Description Ignatius花了一个星期的时间终于找到了传说中的宝藏,宝藏被放在一个房间里,房间的门用密码锁起来了,在门旁边的墙上有一些关于密码的提示信息:密码是 ...

  8. Pairs of Integers

    Pairs of Integers You are to find all pairs of integers such that their sum is equal to the given in ...

  9. polynomial time

    https://en.wikipedia.org/wiki/Time_complexity#Polynomial_time An algorithm is said to be of polynomi ...

  10. 【python】-- 装饰器、迭代器、生成器

    装饰器 装饰器本质是函数,是用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的. 一.装饰器原则: 不能修改被装饰函数的源代码 不能修改被装饰函数的调用方式 def logging(): pr ...