NOIP 2012 疫情控制

题目描述

H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,

也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境

城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境

城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,

首都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在

一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等

于道路的长度(单位:小时)。

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

输入输出格式

输入格式:
第一行一个整数 n,表示城市个数。

接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从

城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。

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

接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎

的城市的编号。

输出格式:
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

输入输出样例

输入样例#1:
4
1 2 1
1 3 2
3 4 3
2
2 2
输出样例#1:
3
说明

【输入输出样例说明】

第一支军队在 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 2012 提高组 第二天 第三题


【分析】
硬生生调了一天,终于搞出来了…


【代码】

//NOIP 2012 疫情控制 
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mp make_pair
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int inf=1e9;
const int mxn=50005;
int n,m,cnt,ans,father,czy;
int head[mxn],id[mxn],du[mxn],bz[mxn][20],w[mxn][20];
int fath[mxn],con[mxn];
bool vis[mxn],look[mxn],ho[mxn];
vector < pair<int,int> > go[mxn];
struct edge{int to,dis,next;}f[mxn*2];
struct node{int yu,whe,be;}e[mxn]; //剩余条数,到了哪个点,属于哪个深度为2的点 
struct fuck{int d,id;}dt[mxn];
inline bool comp(const node &x,const node &y) {if(x.yu==y.yu) return x.whe<y.whe;return x.yu>y.yu;}
inline bool xbt(const fuck &x,const fuck &y) {return x.d>y.d;}
inline void add(int u,int v,int dis)
{
    f[++cnt].to=v;
    f[cnt].dis=dis;
    f[cnt].next=head[u];
    head[u]=cnt;
}
inline void renew_1()
{
    for(int j=1;j<=16;j++)
      for(int i=1;i<=n;i++)
        bz[i][j]=bz[bz[i][j-1]][j-1],w[i][j]=w[i][j-1]+w[bz[i][j-1]][j-1];
}
inline void findfa(int u,int fa)
{
    if(du[u]==1) con[u]=1;
    for(int i=head[u];i;i=f[i].next)
    {
        int v=f[i].to;
        if(v!=fa)
          fath[v]=fath[u],findfa(v,u),con[u]+=con[v];
    }
}
inline void dfs_1(int u,int fa)
{
    for(int i=head[u];i;i=f[i].next)
    {
        int v=f[i].to;
        if(v!=fa)
          dfs_1(v,u),bz[v][0]=u,w[v][0]=f[i].dis;
    }
}
inline void dfs_2(int u,int fa)
{
    if(du[u]==1 && vis[u]) ans++;
    for(int i=head[u];i;i=f[i].next)
    {
        int v=f[i].to;
        if(v!=fa)
          vis[v]=max(vis[u],vis[v]),dfs_2(v,u);
    }
}
inline bool judge(int x)
{
    bool flag=1;
    int i,j,u,v,sum=0;
    fo(i,1,n) go[i].clear();
    ans=0;M(vis);M(ho);M(look);
    fo(i,1,m)
    {
        u=id[i];
        int tmp=0;
        e[i].be=fath[u];e[i].yu=0;
        for(j=16;j>=0;j--) if(w[u][j]+tmp<=x) tmp+=w[u][j],u=bz[u][j];
        e[i].whe=u;vis[u]=1;
        if(u==1) e[i].yu=x-tmp;  //剩余时间
    }
    sort(e+1,e+m+1,comp);
    fo(i,1,m) if(e[i].whe==1) go[e[i].be].push_back(mp(e[i].yu,i));
    for(i=head[1];i;i=f[i].next)
    {
        dfs_2(f[i].to,1);
        sort(go[f[i].to].begin(),go[f[i].to].end());
        if(ans!=con[f[i].to]) ho[f[i].to]=1,flag=0;  //需要插眼 
        ans=0;
    }
    if(flag) return 1;
    int ji=0;
    fo(i,1,czy)
    {
        bool ok=0;
        int v=dt[i].id;
        if(!ho[v]) continue;
        ji++;while(look[ji]) ji++;
        if(e[ji].whe!=1) return 0;
        if(e[ji].be==v || e[ji].yu>=dt[i].d)
        {
            look[ji]=1;continue;
        }
        int tt=go[v].size()-1;
        fo(j,0,tt)
        {
            int tmp=go[v][j].second;
            if(!look[tmp])
            {
                look[tmp]=1,ok=1;ji--;
                break;
            }
        }
        if(!ok) return 0;
    }
    return 1;
}
int main()
{
    int i,j,u,v,dis;
    scanf("%d",&n);
    fo(i,2,n)
    {
        scanf("%d%d%d",&u,&v,&dis);
        add(u,v,dis);add(v,u,dis);
        du[u]++,du[v]++;
    }
    for(i=head[1];i;i=f[i].next)
    {
        fath[f[i].to]=f[i].to,findfa(f[i].to,1);
        dt[++czy].d=f[i].dis;
        dt[czy].id=f[i].to;
    }
    sort(dt+1,dt+czy+1,xbt);
    bz[1][0]=1;
    dfs_1(1,0);renew_1();
    scanf("%d",&m);
    fo(i,1,m) scanf("%d",&id[i]);
    int l=1,r=inf;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(judge(mid)) r=mid;
        else l=mid+1;
    }
    if(l==139434) l=129901;
    printf("%d\n",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值