NOIP2012 疫情控制

来源:互联网 发布:未来计价软件 编辑:程序博客网 时间:2024/05/01 21:32

NOIP2012 疫情控制

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

Hint

【输入输出样例说明】

第一支军队在 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。

Solution:

这题想不到二分的估计也就是我了吧…

首先这个二分还是比较明显的,直接二分最后的答案。然后对于每一个军队,肯定都是尽量往上走是比较好的。

对于一个点,他如果向上走无法走到根节点,那么就让它停在那里,并暴力标记掉它的子树(这一步可以做到均摊O(n),方法是遇到标记过的点就返回)。然后对于能够走到根节点的点,我们先让它“暂存”在根节点上(因为它随时都可以回到来的地方),按照剩余可以行动的距离从小到大排序好。

然后对于前面无法穿过根节点的军队,已经有一些点被覆盖了。我们依次检查根节点的各个儿子,是否这个儿子子树内的叶子节点已都被覆盖。将尚未被完全覆盖的儿子存下来(它们需要跨过根节点的军队帮忙),按照到根的距离也排序好。

然后我们的问题就是如何分配这些军队来覆盖这些节点。我们用两个指针分别指向两个数组。对于一个点,如果它来的地方尚未被覆盖,那么直接让它回去,否则后面用来覆盖整个点的军队能够再前进的距离必然大于等于当前的点,不优。对于剩下的点,随便搞搞就好了。

向上走用倍增,总复杂度是O(nlgnlgMax)

#include<ctime>#include<cstdio>#include<cctype>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#include<set>#include<map>#include<queue>#include<vector>#define ll long long#define lson (p<<1)#define rson (p<<1|1)#define lowbit(x) ((x)&(-x))#define siz(x) (int)((x).size())using namespace std;inline void Rd(int &res){    char c;res=0;    while(c=getchar(),c<'0');    do{        res=(res<<1)+(res<<3)+(c^48);    }while(c=getchar(),c>='0');}const int S=16;const int M=50005;int tot,Head[M],pos[M],fa[S+2][M],depth[M],Q2[M];bool mark[M];ll dis[M];int n,m;ll mx=0;struct Edge{int to,w,nxt;}Edge[M<<1];inline void Addedge(int a,int b,int c){    Edge[++tot].to=b;Edge[tot].w=c;Edge[tot].nxt=Head[a];Head[a]=tot;    Edge[++tot].to=a;Edge[tot].w=c;Edge[tot].nxt=Head[b];Head[b]=tot;}void dfs_pre(int x,int f,ll d,int dep){    if(d>mx)mx=d;    dis[x]=d;    fa[0][x]=f;    depth[x]=dep;    for(int i=Head[x];~i;i=Edge[i].nxt){        int to=Edge[i].to;        if(to==f)continue;        dfs_pre(to,x,d+Edge[i].w,dep+1);    }}int Init(){    for(int i=1;i<=S;i++)        for(int j=1;j<=n;j++)            if(fa[i-1][j])fa[i][j]=fa[i-1][fa[i-1][j]];    int mx=0;    for(int i=Head[1];~i;i=Edge[i].nxt)        if(Edge[i].w>mx)mx=Edge[i].w;    return mx;}int GoUp(int x,ll d){    ll now=dis[x];    for(int i=S;i>=0;i--)        if(fa[i][x]&&fa[i][x]!=1&&now-dis[fa[i][x]]<=d)x=fa[i][x];    return x;}struct Node{    int x;ll ls;    bool operator <(const Node &A)const{        return ls<A.ls;    }}Q1[M];bool cmp(int a,int b){return dis[a]<dis[b];}struct CHECK{    bool flag;    int sz1,sz2;    void dfs_mark(int x){        if(mark[x])return;        mark[x]=true;        for(int i=Head[x];~i;i=Edge[i].nxt){            int to=Edge[i].to;            if(to==fa[0][x])continue;            dfs_mark(to);        }    }    void dfs_check(int x){        if(mark[x])return;        int cc=0;        for(int i=Head[x];~i;i=Edge[i].nxt){            int to=Edge[i].to;            if(to==fa[0][x])continue;            cc++;            dfs_check(to);        }        if(!cc)flag=false;//叶子节点    }    bool check(ll d){        sz1=sz2=0;        memset(mark,0,sizeof(mark));        for(int i=1;i<=m;i++){            int now=GoUp(pos[i],d);            if(depth[now]!=2){//走不到第二层                dfs_mark(now);                continue;            }            ll les=d-dis[pos[i]];            if(les<=0){//无法走到根                dfs_mark(now);                continue;            }            Q1[++sz1]=(Node){now,les};        }        for(int i=Head[1];~i;i=Edge[i].nxt){            int to=Edge[i].to;            flag=true;            dfs_check(to);            if(flag){//叶子节点都已被覆盖                mark[to]=true;                continue;            }            Q2[++sz2]=to;        }        sort(Q1+1,Q1+sz1+1);        sort(Q2+1,Q2+sz2+1,cmp);        int j=1;        for(int i=1;i<=sz1;i++){            while(mark[Q2[j]])j++;            if(j>sz2)return true;//全部匹配完成            int to=Q1[i].x;            if(!mark[to]){//来的点尚未被标记                mark[to]=true;                continue;            }            if(Q1[i].ls>=dis[Q2[j]]){                mark[Q2[j]]=true;                j++;            }        }        for(int i=Head[1];~i;i=Edge[i].nxt){            int to=Edge[i].to;            if(!mark[to])return false;        }        return true;    }}CHECK;int main(){//  freopen("blockade.in","r",stdin);//  freopen("blockade.out","w",stdout);    memset(Head,-1,sizeof(Head));    Rd(n);    for(int i=1;i<n;i++){        int a,b,c;        Rd(a),Rd(b),Rd(c);        Addedge(a,b,c);    }    Rd(m);    for(int i=1;i<=m;i++)Rd(pos[i]);    dfs_pre(1,0,0,1);    ll rs=Init();    rs+=mx;    ll L=0,R=rs,res;//上界为最深的距离加上离根节点最远的一个儿子的距离    while(L<=R){        ll mid=(L+R)>>1;        if(CHECK.check(mid)){            res=mid;            R=mid-1;        }else L=mid+1;    }    cout<<res<<endl;    return 0;}
1 0
原创粉丝点击