疫情控制(noip2012)(二分答案+贪心+倍增)

传送门

最少需要几个小时--->二分答案。

要求每条路径至少一个驻扎点。

贪心得知,军队驻扎点越往上一定更优。

然后树上倍增处理。

一些细节这篇题解写的很好(摘自洛谷题解

4,5,6,7,8就是实现的细节。整道题打下来还是比较考验代码能力和思维的。

#include<bits/stdc++.h>
#define LL long long 
#define INF 2100000000
#define N 500003
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct EDEG{
    int nextt,to,val;
}w[N*2];
int n,m; 
int head[N],f[N][23],dep[N],id[N];
LL dis[N][23];
int tot=0;
void add(int a,int b,int c)
{
    tot++;
    w[tot].nextt=head[a];
    w[tot].to=b;
    w[tot].val=c;
    head[a]=tot;
}
void dfs1(int x,int fa)//倍增预处理 
{
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(v==fa)continue;
        dep[v]=dep[x]+1;
        f[v][0]=x;
        dis[v][0]=w[i].val;
        for(int j=1;j<=20;++j)
        {
            f[v][j]=f[f[v][j-1]][j-1];
            dis[v][j]=dis[v][j-1]+dis[f[v][j-1]][j-1];
        }
        dfs1(v,x); 
    }
}
int xtot=0;//闲置军队个数
pair<LL,int>fre[N];//闲置军队 
int sta[N];//驻扎军队 
int need[N];//处理闲置军队前需要驻扎军队的地方
int ned[N],btot=0;//处理闲置军队后需要驻扎军队的地方
int tim[N],atot=0;//还可以用的闲置军队 
bool dfs(int x)//判断某条路径是否还没有军队驻扎,从根到叶子 
{
    int not_leaf=0;
    if(sta[x])return 1;
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(dep[v]<dep[x])continue;
        not_leaf=1;
        if(!dfs(v))return 0;
    }
    if(!not_leaf)return 0;//当前到了叶子也没有驻扎 
    return 1;
}
bool check(LL mid)
{
    memset(sta,0,sizeof(sta));
    memset(need,0,sizeof(need));
    memset(ned,0,sizeof(ned));
    memset(tim,0,sizeof(tim));
    memset(fre,0,sizeof(fre));
    atot=0;btot=0;xtot=0;
    for(int k=1;k<=m;++k)
    {
        int x=id[k];
        LL cnt=0;
        for(int i=20;i>=0;--i)
        {
            if(f[x][i]>1&&cnt+dis[x][i]<=mid)
            {
                cnt+=dis[x][i];
                x=f[x][i];
            }
        }
        if(f[x][0]==1&&cnt+dis[x][0]<=mid)//可作为闲置军队 
          fre[++xtot]=make_pair(mid-cnt-dis[x][0],x);
        else sta[x]=1;
    }
    for(int i=head[1];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(!dfs(v))need[v]=1;
    }
    
    sort(fre+1,fre+1+xtot);//根据第一关键字,即闲置军队余下的时间 
    for(int i=1;i<=xtot;++i) 
    {
        if(need[fre[i].second]&&fre[i].first<dis[fre[i].second][0])
          need[fre[i].second]=0;
        else tim[++atot]=fre[i].first;
        //对于第5步中需要被驻扎的节点,若该节点上有军队停留,则对于这些军队中剩余时间最小的军队,
        //若该军队的剩余时间不足够令它从根节点再返回现在停留的节点,则直接驻扎在该节点是最优的。
        //因为该军队无法从当前的节点到达根节点再返回,说明若它驻扎在根节点的其它子树,
        //只能驻扎在一个到根节点的距离小于当前节点到根节点的距离的节点。而这个节点所有剩余时间比它多的军队一定也可以去驻扎,
        //甚至还可能驻扎到其它更多的节点。因此不如让剩余时间多的节点去驻扎,而自己留在当前节点即可。
    }
    for(int i=head[1];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(need[v])ned[++btot]=dis[v][0];
    }
    if(atot<btot)return 0;
    sort(tim+1,tim+1+atot);sort(ned+1,ned+1+btot); 
    int i=1,j=1;
    while(i<=btot&&j<=atot)
    {
        if(tim[j]>=ned[i])i++,j++;
        else j++;
        
    }
    if(i>btot)return 1;
    return 0;
}
int main()
{
    n=read();
    LL l=0,r=0;
    for(int i=1;i<n;++i)
    {
        int a=read(),b=read(),c=read();
        r+=c;
        add(a,b,c);add(b,a,c); 
    }
    dep[1]=1;
    for(int i=0;i<=20;++i)
      f[1][i]=1;
    dfs1(1,1);
    m=read();
    for(int i=1;i<=m;++i)id[i]=read();
    LL ans=0;int flagg=0;
    while(l<r)
    {
        LL mid=(l+r)>>1;
        if(check(mid))flagg=1,ans=mid,r=mid;
        else l=mid+1;
    }
    if(flagg)printf("%lld\n",ans);
    else printf("-1\n"); 
} 
View Code

猜你喜欢

转载自www.cnblogs.com/yyys-/p/11712701.html
今日推荐