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

1218 疫情控制 2012年NOIP全国联赛提高组
时间限制: 2 s
空间限制: 128000 KB
题目等级 : 钻石 Diamond
题目描述 Description
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入描述 Input Description
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从
城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎
的城市的编号。
输出描述 Output Description
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
样例输入 Sample Input
4
1 2 1
1 3 2
3 4 3
2
2 2
样例输出 Sample Output
3
数据范围及提示 Data Size & Hint
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 10;
对于 40%的数据,2 ≤n≤50,0

/*
这题太好了!!!
二分+lca+贪心策略.
二分[1,∑w].
先dfs序+lca处理出各点关系.
然后二分检验.
贪心策略:
(1)向上走更优.
(2)排序后能最大匹配.
我们先来考虑几种情况 
(1)这个军队能够蹦到根节点.
   我们处理出蹦到根节点后剩余的时间. 
(2)这个军队蹦不到根节点
   我们尽量让他往上蹦.
然后向上传标记.
最后只需要扫根节点的儿子就可以了.
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 50001
#define D 20
using namespace std;
int fa[MAXN][D+5],deep[MAXN],son[MAXN],head[MAXN],n,m,dis[MAXN],a[MAXN],tot,ans=1e9,top,cut,sum;
bool b[MAXN];
struct data{int v,u;}s[MAXN],c[MAXN];
struct egde{int v,next,x;}e[MAXN*2];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x*f;
}
void add(int u,int v,int z)
{
    e[++tot].v=v;
    e[tot].x=z;
    e[tot].next=head[u];
    head[u]=tot;
}
bool cmp(const data &x,const data &y)
{
    return x.v<y.v;
}
void dfs(int now,int father,int d)
{
    fa[now][0]=father;
    for(int i=head[now];i;i=e[i].next)
    {
        if(father!=e[i].v)
        {
            dis[e[i].v]=dis[now]+e[i].x;
            dfs(e[i].v,now,d+1);
        }
    }
}
void get_father()
{
    for(int j=1;j<=20;j++)
      for(int i=1;i<=n;i++)
        fa[i][j]=fa[fa[i][j-1]][j-1];
}
void slove(int u,int fa)
{
    bool flag=false,p=1;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(fa!=v)
        slove(v,u),p&=b[v],flag=true;
    }
    if(flag&&p&&u!=1) b[u]=true;return ;
}
bool check(int x)
{
    memset(b,0,sizeof(b));
    int father=0;top=0,cut=0;
    for(int i=1;i<=m;i++)
    {
        int now=a[i],d=0;
        if(dis[now]<x)
            father=1;
        else for(int j=20;j>=0;j--)
        {
            if(fa[now][j]&&dis[now]-dis[fa[now][j]]<=x)
            {
                father=fa[now][j];
                d=dis[now]-dis[fa[now][j]];
                break;
            }
        }
        if(father!=1) b[father]=true;
        else
        {
            s[++top].v=x-dis[now];
            for(int j=20;j>=0;j--)
                if(fa[now][j]>1)
                    now=fa[now][j];
            s[top].u=now;
        }
    }
    slove(1,0);
    for(int i=head[1];i;i=e[i].next)
      if(!b[e[i].v])
        c[++cut].u=e[i].v,c[cut].v=e[i].x;
    sort(s+1,s+top+1,cmp),sort(c+1,c+cut+1,cmp);
    int j=1;c[cut+1].v=1e9;
    for(int i=1;i<=top;i++)
    {
        if(!b[s[i].u]) b[s[i].u]=true;
        else if(s[i].v>=c[j].v) b[c[j].u]=true;
        while(b[c[j].u]) j++;
    }
    return j>cut;
}
void erfen(int ll,int rr)
{
    int mid;
    while(ll<=rr)
    {
        mid=(ll+rr)>>1;
        if(check(mid)) ans=mid,rr=mid-1;
        else ll=mid+1;
    }
}
int main()
{
    int x,y,z;
    n=read();
    for(int i=1;i<n;i++)
    {
        x=read(),y=read();z=read();
        add(x,y,z);sum+=z;add(y,x,z);
    }
    m=read();
    for(int i=1;i<=m;i++)
      a[i]=read();
    dfs(1,0,0);
    get_father();
    erfen(1,sum);
    if(ans==1e9) printf("-1");
    else printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/nancheng58/p/6070756.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值