码迷,mamicode.com
首页 > 其他好文 > 详细

P1084 疫情控制

时间:2019-09-08 22:11:00      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:html   倍增   检查点   vertica   long   check   end   子节点   时间   

题目描述

HHH 国有 nn n个城市,这 nnn 个城市用n−1 n-1 n1条双向道路相互连通构成一棵树,11 1号城市是首都,也是树中的根节点。

HH H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。

现在,在 HHH 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。

请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

输入格式

第一行一个整数n nn,表示城市个数。

接下来的 n−1n-1n1 行,每行3 3 3个整数,u,v,wu,v,wu,v,w,每两个整数之间用一个空格隔开,表示从城市 uu u到城市v vv 有一条长为 www 的道路。数据保证输入的是一棵树,且根节点编号为 111。

接下来一行一个整数 mmm,表示军队个数。

接下来一行 mm m个整数,每两个整数之间用一个空格隔开,分别表示这 mmm 个军队所驻扎的城市的编号。

输出格式

一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出−1-11。

输入输出样例

输入 #1
4 
1 2 1 
1 3 2 
3 4 3 
2 
2 2
输出 #1
3

说明/提示

【输入输出样例说明】

第一支军队在 222 号点设立检查点,第二支军队从 222 号点移动到3 33 号点设立检查点,所需时间为 333 个小时。

【数据范围】

保证军队不会驻扎在首都。

对于 20%的数据,2≤n≤102≤ n≤ 102n10;

对于 40%的数据,2≤n≤50,0<w<1052 ≤n≤50,0<w <10^52n50,0<w<105;

对于 60%的数据,2≤n≤1000,0<w<1062 ≤ n≤1000,0<w <10^62n1000,0<w<106;

对于 80%的数据,2≤n≤10,0002 ≤ n≤10,0002n10,000;

对于 100%的数据,2≤m≤n≤50,000,0<w<1092≤m≤n≤50,000,0<w <10^92mn50,000,0<w<109。

NOIP 2012 提高组 第二天 第三题

思路

对于每一个军队,我们将它越往上走效果越好,所以每个军队应该尽可能往上,所以题目转化为求最大值最小的问题。因而可以使用二分答案,转换为判断是否能将树覆盖。因此我们二分时间,在范围内朝根前进。若能达到根,则停在那不动。所以可以倍增上眺。

预处理:

void dfs(int x,int f,int z){
    dep[x]=dep[f]+1;
    fa[x][0]=f;dis[x][0]=z;//祖先
    for(int i=1;(1<<i)<=dep[f];i++){
        fa[x][i]=fa[fa[x][i-1]][i-1];
        dis[x][i]=dis[x][i-1]+dis[fa[x][i-1]][i-1];//距离
    }
    for(int i=head[x];i;i=e[i].next)
        if(e[i].y!=f) dfs(e[i].y,x,e[i].z);
}

上跳并记录到根的军队剩余时间:

   cntb=cntr=0;
   for(int i=1;i<=n;i++) vis[i]=0;
   int x,num;
   for(int i=1;i<=m;i++){
       x=a[i],num=0;
       for(int j=17;j>=0;j--)
           if(fa[x][j]>1&&num+dis[x][j]<=lim) 
               num+=dis[x][j],x=fa[x][j];
       if(fa[x][0]==1&&num+dis[x][0]<=lim){
           b[++cntb].r=lim-num-dis[x][0];
           b[cntb].id=x;
       }else vis[x]=1;
}

之后遍历,将无军队的子树上升到根的子树,并记录距离为rest(因为从根到子树中根的下一个节点为最优)

int checkok(int x,int f){
    if(vis[x]) return 1;
    int q=0;vis[x]=1;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y!=f) q=1,vis[x]=min(checkok(y,x),vis[x]);
    }
    vis[x]=min(vis[x],q);
    return vis[x];
}
if(checkok(1,0)) return 1;
    for(int i=head[1];i;i=e[i].next)
        if(!vis[e[i].y]) rest[++cntr].r=e[i].z,rest[cntr].id=e[i].y;
    if(cntb<cntr) return 0;
    sort(b+1,b+cntb+1,cmp);sort(rest+1,rest+cntr+1,cmp);
    int kk=1;

最后从小到大依次比较,若b[i]比所以rest[i]小,则去其他节点无意义,应该回到原来节点。

    int kk=1;
    for(int i=1;i<=cntr;i++){
        if(vis[rest[i].id]) continue;
        while(b[kk].r<rest[i].r&&kk<=cntb&&vis[rest[i].id]!=1) vis[b[kk].id]=1,kk++;
        if(kk==cntb+1) return 0;
        if(!vis[rest[i].id]) vis[rest[i].id]=1,kk++;
    }
    return 1;

代码

#include<bits/stdc++.h>
#define N 57000
using namespace std;
struct node{
    int y,z,next;
}e[N<<1];
int n,m,head[N],tot;
void add(int x,int y,int z){
    e[++tot].y=y;e[tot].z=z;
    e[tot].next=head[x];head[x]=tot;
}
int fa[N][67],dep[N],a[N];
long long dis[N][67];
void dfs(int x,int f,int z){
    dep[x]=dep[f]+1;
    fa[x][0]=f;dis[x][0]=z;
    for(int i=1;(1<<i)<=dep[f];i++){
        fa[x][i]=fa[fa[x][i-1]][i-1];
        dis[x][i]=dis[x][i-1]+dis[fa[x][i-1]][i-1];
    }
    for(int i=head[x];i;i=e[i].next)
        if(e[i].y!=f) dfs(e[i].y,x,e[i].z);
}
int vis[N],cntb,ji,cntr;
struct P{
    int id,r;
}b[N],rest[N];
bool cmp(P i,P j){ return i.r<j.r; }
int checkok(int x,int f){
    if(vis[x]) return 1;
    int q=0;vis[x]=1;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y!=f) q=1,vis[x]=min(checkok(y,x),vis[x]);
    }
    vis[x]=min(vis[x],q);
    return vis[x];
}
bool check(int lim){
    cntb=cntr=0;
    for(int i=1;i<=n;i++) vis[i]=0;
    int x,num;
    for(int i=1;i<=m;i++){
        x=a[i],num=0;
        for(int j=17;j>=0;j--)
            if(fa[x][j]>1&&num+dis[x][j]<=lim) 
                num+=dis[x][j],x=fa[x][j];
        if(fa[x][0]==1&&num+dis[x][0]<=lim){
            b[++cntb].r=lim-num-dis[x][0];
            b[cntb].id=x;
        }else vis[x]=1;
    }
    if(checkok(1,0)) return 1;
    for(int i=head[1];i;i=e[i].next)
        if(!vis[e[i].y]) rest[++cntr].r=e[i].z,rest[cntr].id=e[i].y;
    if(cntb<cntr) return 0;
    sort(b+1,b+cntb+1,cmp);sort(rest+1,rest+cntr+1,cmp);
    int kk=1;
    for(int i=1;i<=cntr;i++){
        if(vis[rest[i].id]) continue;
        while(b[kk].r<rest[i].r&&kk<=cntb&&vis[rest[i].id]!=1) vis[b[kk].id]=1,kk++;
        if(kk==cntb+1) return 0;
        if(!vis[rest[i].id]) vis[rest[i].id]=1,kk++;
    }
    return 1;
}
int main()
{
    int x,y,z,l=0,r=500000,ans=-1;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]);
    for(int i=head[1];i;i=e[i].next) ji++;
    if(ji>m){cout<<"-1"<<endl;return 0;} 
    dfs(1,0,0);
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid-1,ans=mid;
        else l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

 

P1084 疫情控制

标签:html   倍增   检查点   vertica   long   check   end   子节点   时间   

原文地址:https://www.cnblogs.com/wangyiding2003/p/11488634.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!