[noip2012]疫情控制

Posted kgxw0430

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[noip2012]疫情控制相关的知识,希望对你有一定的参考价值。

noip2012

  • 都这个时候了我居然还在水noip题,而且写了一上午(看题解看代码)终于改对233。
  • 题意简述:有(m)支军队,每个军队在某一个点上。如果某一个点上有军队,那么这个点可以建立检查站。检查站的子树上所有点都可以被覆盖。问,最短需要多长时间,所有的叶子节点都能被覆盖。(注意,根节点不可以建立检查站)。
  • 直接求不好求,答案显然具有单调性,先二分答案。
  • 首先,每一个点在到达根节点之前,向上爬一定最优。如果一个点爬到1号点还有剩余时间,那么把它入栈。它们用来解决什么情况吧?就是1的儿子里可能某棵子树上有的叶子节点未被覆盖。
  • dfs看一下哪些儿子(1的儿子)的子树里还有未被覆盖的叶子节点,这些点需要用栈里的点来覆盖。
  • 怎样覆盖是最优方案?借助贪心的思路,我们把栈中的点和需要被覆盖的点,按照距离从大到小排序。假如当前处理到了第(i)个点,即(1-(i-1))的点都已经覆盖过了,如果这个点可以回去,(即这个点原本可以被覆盖,只不过我们让他跑到了1号节点上),就让他回去。否则,拿当前的栈中从左到右第一个没被使用过的点尝试覆盖当前点,如果不能覆盖,说明无解。
  • 这样一定是最优的覆盖方案,思考为什么?
  • 假如此时我们要覆盖(i),栈中的点我们使用到了第(c)个。如果此时(i)可以回去,说明(i)这个点在栈中,并且它一定在(c)的后面。这时候我们让他回去,相当于用了一个小的代价代替大的代价,帮我们覆盖了这个点。(c)的这个步数更多的点,就可以尝试去覆盖后面不容易覆盖的点。因此,贪心策略是对的。
  • 注意数组范围等细节处理。

    Coding

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e5+100;
ll n,m,tot,a[N],rd[N],v[N],ver[N<<1],Next[N<<1],lin[N],edge[N],f[N][35];ll g[N][35];
ll used[N],top1,top2;
ll zx[N];ll rx[N];
bool flag;
struct node{
    ll rest;ll id;
}c[N],d[N];
ll read(){
    char ch=getchar();ll num=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
    return num*f;
}
void add(ll x,ll y,ll z){ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;edge[tot]=z;rd[y]++;}
bool cmp(node x,node y){return x.rest>y.rest;}
void dfs(ll x,ll fa,ll id){
    f[x][0]=fa;g[x][0]=edge[id];
    for(ll i=1;i<=20;++i) f[x][i]=f[f[x][i-1]][i-1],g[x][i]=(ll)(g[x][i-1]+g[f[x][i-1]][i-1]);
    for(ll i=lin[x];i;i=Next[i]){
        ll y=ver[i];
        if(y==fa) continue;
        dfs(y,x,i);
    }
}
void Dfs(ll x,ll fa,ll now){
    if(v[x]) {now=1;return;}
    if(now==0&&rd[x]==1){ flag=0;return ;}
    for(ll i=lin[x];i;i=Next[i]){
        ll y=ver[i];
        if(y==fa) continue;
        Dfs(y,x,now);
    }
}
bool check(ll mid){
    memset(v,0,sizeof(v));
    memset(rx,0,sizeof(rx));
    memset(zx,0,sizeof(zx));
    memset(used,0,sizeof(used));
    top1=0,top2=0;
    for(ll i=1;i<=m;++i){
        ll x=a[i],num=0;
        for(ll j=20;j>=0;--j)//3.上提军队
            if(f[x][j]>1&&num+g[x][j]<=mid)
            num+=g[x][j],x=f[x][j];
        if(f[x][0]==1&&num+g[x][0]<=mid){//4.处理剩余路程
            c[++top1].rest=mid-num-g[x][0],c[top1].id=i;
            if(!zx[x]||c[top1].rest<rx[x])
                rx[x]=c[top1].rest,zx[x]=i;
        }
        else v[x]=1;
    }
    for(ll i=lin[1];i;i=Next[i]){
        ll y=ver[i];
        flag=1;Dfs(y,1,0);
        if(!flag) d[++top2].rest=edge[i],d[top2].id=y;
    }
    sort(c+1,c+top1+1,cmp);
    sort(d+1,d+top2+1,cmp);
    ll now=1;used[0]=1;
    for(ll i=1;i<=top2;++i){
        if(!used[zx[d[i].id]]&&zx[d[i].id]){used[zx[d[i].id]]=1;continue;}
        while(now<=top1&&(used[c[now].id]||c[now].rest<d[i].rest))++now;
        if(now>top1)return 0;used[c[now].id]=1;
    }
    return 1;
}
int main(){
    //freopen("1.in","r",stdin);
    n=read();//memset(g,0x3f,sizeof(g));
    for(ll i=1;i<n;++i){
        ll x=read(),y=read(),z=read();
        add(x,y,z);add(y,x,z);
    }
    m=read();
    for(ll i=1;i<=m;++i) a[i]=read();
    dfs(1,0,0);
    ll l=0,r=1e15;
    while(l+1<r){
        ll mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    if(check(l)) printf("%lld
",l);
    else printf("%lld
",r);
    return 0;
}

以上是关于[noip2012]疫情控制的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2012 疫情控制

刷题总结——疫情控制(NOIP2012提高组)

NOIP2012 疫情控制

NOIP2012 疫情控制

noip2012 疫情控制

CodeVS 1218NOIP 2012疫情控制