【NOIP2012提高组】疫情控制

Description

给出一颗n个节点有边权的树 和m个军队所在的位置 军队从某节点移动到相邻节点要花费边长度的时间 求最少要多少时间使得根节点(编号为1)到每个叶子的路径上最少有一支军队(根节点不能有军队)

Solution

这题第一眼看上去就像树形DP,然而想复杂了。

二分答案

求什么最大或最小之类的,肯定要用二分。

小贪心

我们发现每个军队走得越上,贡献越大。很显然。

怎么做

既然是越往上越优,那么我们都往上。
现在就分两种情况了:
1、这支军队到达不了根节点:那么我们就到达他最多能到的节点,然后打一个标记;
2、这支军队能到达根节点:我们把所有能到达根节点的点统计出来,放在一个数组里面,排一个序,然后我们把根节点没打标记的子节点放在另一个数组里面,排一个序,最后逐个的比较就好了。
注意:如果当前这个军队从y走到了根节点,然后派到了其他地方,但是别的节点到不了y,这个时候我们可以把当前这支军队派到y就好了。还要注意,我们标记之后要把整棵树重新标记一下——>如果一个节点的所有子节点都被标记了,那么这个点就要被标记。

至于在树上跳点

打一个倍增就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
typedef long long ll;
const int maxn=100007;
using namespace std;
ll i,j,k,l,t,n,m,ans,r,mid,yi,er;
ll a[maxn];
ll first[maxn*2],next[maxn*2],last[maxn*2],chang[maxn*2],num;
ll b[maxn],tot,f[maxn][18],g[maxn][18],deep[maxn];
ll c[maxn],d[maxn],e[maxn],wwa,vv;
bool bz[maxn],az[maxn];
struct node{
    ll w,v;
}w[maxn];
struct nod{
    ll a,b;
}v[maxn];
void add(ll x,ll y,ll z){
    last[++num]=y;
    next[num]=first[x];
    first[x]=num;
    chang[num]=z;
}
void dfs(ll x,ll y){
    int i,j,k;
    for(i=first[x];i;i=next[i]){
        if(last[i]!=y){
            f[last[i]][0]=x;
            g[last[i]][0]=chang[i];
            dfs(last[i],x);
        }
    }
}
void suan(ll x,ll y){
    int i,j,k=0;
    if(k+g[x][0]<=y&&f[x][0]){
        x=f[x][0];
        k+=g[x][0];
    }
    yi=x;er=k;
}
bool cmp(nod x,nod y){
    return x.a<y.a;
}
bool cmp1(node x,node y){
    return x.w<y.w;
}
void dfs1(ll x){
    bool dz=1,ez=1;
    for(int i=first[x];i;i=next[i]){
        if(last[i]!=f[x][0]){
            dfs1(last[i]);
            dz=0;
            if(bz[last[i]]==0)ez=0;
        }  
    }
    if(ez&&x!=1&&dz==0)bz[x]=1;
}
bool pan(ll x){
    ll i,j,p,q,o;
    wwa=vv=0;
    memset(bz,0,sizeof(bz));
    fo(i,1,m){
        o=0;
        q=a[i];p=0;
        fod(j,17,0){
            if(p+g[q][j]<=x&&f[q][j]){
                p+=g[q][j];
                q=f[q][j];
            }
        }
        if(q!=1){
            bz[q]=1;
        }
        else{
            w[++wwa].w=x-p;
            q=a[i];
            fod(j,17,0)if(f[q][j]>1)q=f[q][j];
            w[wwa].v=q;
        }
    }
    dfs1(1);
    sort(w+1,w+1+wwa,cmp1);
    for(i=first[1];i;i=next[i]){
        if(!bz[last[i]]){
            v[++vv].a=chang[i];  
            v[vv].b=last[i];
        }
    }
    sort(v+1,v+1+vv,cmp);
    if(wwareturn 0;
    v[vv+1].a=0x7fffffff;
    k=1;
    fo(i,1,wwa){
        if(!bz[w[i].v])bz[w[i].v]=1;
        else if(w[i].w>=v[k].a){
            bz[v[k].b]=1;
            k++;
        }
        while(bz[v[k].b])k++;
    }
    if(k>vv)return 1;
    else return 0;
}
int main(){
    scanf("%lld",&n);
    fo(i,1,n-1){
        scanf("%lld%lld%lld",&k,&l,&t);
        add(k,l,t);
        add(l,k,t);
    }
    dfs(1,0);
    fo(j,1,17){
        fo(i,1,n){
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=g[f[i][j-1]][j-1]+g[i][j-1];
        }
    }
    scanf("%lld",&m);
    fo(i,1,m){
        scanf("%lld",&a[i]);
    }
    l=0;r=1000000000;
    while(l2;
        if(pan(mid))r=mid;else l=mid+1;
    }
    printf("%lld\n",l);
}

你可能感兴趣的:(noip,倍增算法,二分)