P1084 疫情控制

Posted wangyiding2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1084 疫情控制相关的知识,希望对你有一定的参考价值。

题目描述

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 疫情控制的主要内容,如果未能解决你的问题,请参考以下文章

P1084 疫情控制

P1084 疫情控制

Luogu P1084 疫情控制 | 二分答案 贪心

P1084 疫情控制

洛谷P1084 疫情控制

洛谷 P1084 疫情控制 题解