JZOJ 3104【NOIP2012提高组】疫情控制

来源:互联网 发布:数易生日23:00后计算法 编辑:程序博客网 时间:2024/05/22 00:25

Description

给出一棵n个节点的树,根是1,要在除根节点以外的点建立检查点,使得从每条根到叶子的路径上都至少存在一个检查点。检查点由军队来建立。初始军队的位置是给定的,移动军队走一条边需要花费这条边的权值的时间。现在要求一个方案,移动军队到某个最佳位置,使得总用时最少。

Analysis

控制疫情的时间取决于时间最长的军队,而题目要求总用时最少,最大值最小就可以二分了。
为了方便叙述,我们称根节点的子节点为目标点。
首先二分答案lim
然后,在lim个单位时间之内,所有军队肯定越往上走越优。
所以可以倍增走。这样就是O(log2maxansmlogn)了。
然后军队分两种情况:能走到根的和走不到根的。
对于走不到根的,把每个军队可以走到的最高点都标个号。如果一个节点所有子节点都标了号或该节点自身标了号,就说明是可以控制的节点。然后如果有目标点被控制了,就不用理它了。
剩下就交给走到根的军队。
对于这些,算出他们剩余的时间,求出他们所属的目标点。
然后求出所有未控制的目标点到根的长度,与军队匹配。注意匹配时有些特殊情况需要额外考虑。

Code

#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,b,a) for(int i=b;i>=a;i--)using namespace std;typedef long long ll;const int N=50005;int n,m,tot,to[N*2],next[N*2],last[N];int numx,numy,a[N],b[N],c[N],deep[N],f[N][16];ll wei[N*2],g[N][16];struct node{    int v;    ll w;}x[N],y[N];bool bz[N];bool cmp(node a,node b){return a.w<b.w;}void link(int u,int v,ll w){    to[++tot]=v;    wei[tot]=w;    next[tot]=last[u];    last[u]=tot;}void dfs1(int v,int from,ll k,int dep,int S){    b[v]=S,deep[v]=dep;    for(int i=last[v];i;i=next[i])    {        int u=to[i];        if(u==from) continue;        f[u][0]=v,g[u][0]=wei[i];        dfs1(u,v,k+wei[i],dep+1,S);    }}int DA(int v,int S,ll lim){    fd(i,int(log2(deep[v])),0)        if(deep[f[v][i]] && lim>=g[v][i]) lim-=g[v][i],v=f[v][i];    if(v==1) x[++numx].v=S,x[numx].w=lim;    return v;}void dfs2(int v,int from){    bool p=1,q=0;    for(int i=last[v];i;i=next[i])    {        int u=to[i];        if(u==from) continue;        q=1;        dfs2(u,v);        if(!bz[u]) p=0;    }    if(p && q) bz[v]=1;}bool check(ll lim){    memset(bz,0,sizeof(bz));    numx=0;    fo(i,1,m)    {        int v=a[i];        int u=DA(v,b[v],lim);        bz[u]=1;    }    dfs2(1,1);    fo(i,1,numx)    {        int v=x[i].v;        int S=b[v];        if(!bz[S] && x[i].w<c[S])        {            bz[S]=1;            x[i].v=0,x[i].w=-1;        }    }    numy=0;    for(int i=last[1];i;i=next[i])    {        int u=to[i];        if(!bz[u]) y[++numy].v=u,y[numy].w=wei[i];    }    sort(x+1,x+numx+1,cmp);    sort(y+1,y+numy+1,cmp);    int i=1,j=1;    while(j<=numy)    {        if(bz[y[j].v])        {            j++;            continue;        }        if(i>numx) break;        if(x[i].w>=y[j].w) i++,j++;        else bz[x[i++].v]=1;    }    if(j>numy) return 1;    return 0;}int main(){    int u,v;    ll w,l=0,r=0,mid;    scanf("%d",&n);    fo(i,1,n-1)    {        scanf("%d %d %lld",&u,&v,&w);        r+=w;        link(u,v,w),link(v,u,w);    }    deep[1]=1;    for(int i=last[1];i;i=next[i])    {        u=to[i];        c[u]=wei[i];        f[u][0]=1,g[u][0]=wei[i];        dfs1(u,1,wei[i],2,u);    }    fo(j,1,int(log2(n)))        fo(i,1,n)        {            f[i][j]=f[f[i][j-1]][j-1];            g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1];        }    scanf("%d",&m);    fo(i,1,m) scanf("%d",&a[i]);    while(l<r)    {        mid=(l+r)>>1;        if(check(mid)) r=mid;        else l=mid+1;    }    printf("%lld",l);    return 0;}
0 0