1. 程式人生 > >【二分+貪心+倍增】【NOIP2012】疫情控制

【二分+貪心+倍增】【NOIP2012】疫情控制

多少 bsp 節點 out num 輸出 註意 至少 二分

Description

H國有n個城市,這n個城市用n-1條雙向道路相互連通構成一棵樹,1號城市是首都,也是樹中的根節點。 H國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要註意的是,首都是不能建立檢查點的。 現在,在H國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連接的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等於道路的長度(單位:小時)。 請問最少需要多少個小時才能控制疫情。註意:不同的軍隊可以同時移動。

Input Format

第一行一個整數n,表示城市個數。 接下來的n-1行,每行3個整數,u、v、w,每兩個整數之間用一個空格隔開,表示從城市u到城市v有一條長為w的道路。數據保證輸入的是一棵樹,且根節點編號為1。 接下來一行一個整數m,表示軍隊個數。 接下來一行m個整數,每兩個整數之間用一個空格隔開,分別表示這m個軍隊所駐紮的城市的編號。

Output Format

共一行,包含一個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出-1。

Sample Input

4 
1 2 1 
1 3 2 
3 4 3 
2 
2 2 

Sample Output

3

Hint

保證軍隊不會駐紮在首都。

對於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

因為軍隊同時進行,且邊有權值,
每支軍隊走完一條邊剩下的時間不同,
所以最少時間一定是最後一個軍隊的停下的時間。
很顯然可以二分答案。
驗證答案時,要用到貪心的思想,
首先對於每支軍隊,向上走能控制的點就越多,

所以每支軍隊都應該向上走,
不能走到根節點的軍隊,就駐紮在停下來的點,
並做上標記,最後計算沒被控制的子樹到達根節點的距離,
對於能走到根節點的點,記錄每支軍隊的剩余時間,我們考慮是否走到其他子樹,
把可以走到根節點的軍隊的剩余時間以及沒被控制的子樹到達根節點的距離從小到大排序
然後貪心,剩余時間少的軍隊走距離根節點距離小的子樹,
對於能走到根節點無法回到原子樹的軍隊,我們選擇留在原子樹,
因為如果選擇其他子樹,說明其他子樹距離根節點的距離小於原子樹到達根節點距離,
所以軍隊選擇其他子樹,並不比留在原子樹優。

#include<cstdio>
#include<cstring>
#include<algorithm>

int n,m;
int num,head[50005],army[50005],f[50005][20];
long long dis[50005][20],l,r,mid,ans=0;
bool se[50005];

struct node
{
    int pos,dis;
}A[50005],C[50005];

struct edge
{
    int to,next,dis;
}e[100005];

void add(int x,int y,int z)
{
    e[++num].next=head[x];
    e[num].to=y;
    e[num].dis=z;
    head[x]=num;
}

bool cmp(node a,node b)
{
    return a.dis<b.dis;
}

void dfs(int x,int fa)
{
    f[x][0]=fa;
    for (int i=1;i<17;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
        dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1];
    }
    for (int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if (v==fa) continue;
        dis[v][0]=e[i].dis;
        dfs(v,x);
    }
}

int up(int x,long long &dist)
{
    for (int i=16;i>=0;i--)
        if (f[x][i]>1&&dist+dis[x][i]<=mid) dist+=dis[x][i],x=f[x][i];
    return x;
}

bool go(int x,int fa)
{
    if (se[x]) return 1;
    int ned=0;
    for (int i=head[x];i&&ned<3;i=e[i].next) ned++;
    if (ned==1&&e[head[x]].to==fa) return se[x]=0;
    for (int i=head[x];i;i=e[i].next)
    {
        if (e[i].to==fa) continue;
        if (!go(e[i].to,x)) return se[x]=0;
    }
    return se[x]=1;
}

bool check(int x)
{
    memset(se,0,sizeof se);
    int nn=0,na=0;
    for (int i=1;i<=m;i++)
    {
        long long dist=0;
        int anc=up(army[i],dist);
        if (f[anc][0]==1&&(dist+dis[anc][0])<mid)
            A[++na].pos=anc,A[na].dis=mid-dist-dis[anc][0];
        else se[anc]=true;
    }
    for (int i=head[1];i;i=e[i].next)
    {
        int v=e[i].to;
        if (!go(v,1)) C[++nn].pos=v,C[nn].dis=e[i].dis;
    }
    if (nn>na) return 0;
    std::sort(A+1,A+na+1,cmp),std::sort(C+1,C+nn+1,cmp);
    int j=1;
    for(int i=1;i<=na;i++)
    {
        if (!se[A[i].pos]) se[A[i].pos]=true;
        else if(C[j].dis<=A[i].dis) se[C[j].pos]=true;
        while (se[C[j].pos]&&j<=nn) j++;
    }
    return j>nn;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        r+=z;
        add(x,y,z),add(y,x,z);
    }
    scanf("%d",&m);
    for (int i=1;i<=m;i++) scanf("%d",&army[i]);
    int notre=0;
    for (int i=head[1];i;i=e[i].next) notre++;
    if (notre>m)
    {
        printf("-1");
        return 0;
    }
    dfs(1,0);
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (check(mid))
        {
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    printf("%lld",ans);
}

【二分+貪心+倍增】【NOIP2012】疫情控制