• NOIP2012疫情控制 二分答案+贪心+树上倍增


    NOIP2012疫情控制

    细节比较多,思维难度还是有的(主要是考虑情况的完整性),可能重点在实现上面吧。

    Solution:

    最少需要多少? 很明显的可以二分答案是吧。

    然后重点考虑如何check。可以来模拟一下这个过程:

    首先,一个很显然的贪心策略: 一支军队能往上走就尽量往上走。

    由于现在知道每支军队能够向上走多少步,

    所以可以用树上倍增,把每支军队跳到根节点下或停在它不能继续向上的位置。

    这个时候进行一次统计,看还有多少个叶子节点没有被覆盖的掉,记录一下它的距离根最近的祖先,即根的某个儿子。

    为什么记录它是很显然的:它所在的子树还有未被覆盖的叶子节点,那么必然需要其他子树中的过来,显然走到它就停下是最优的。

    无解的情况:记录的点数>多余的军队数。

    然后,

    I:会了会了,直接把所有这样的根的儿子找出来后,再统计一下其他子树有多少可以空出来。
    
        都用个数组存着,从小到大排个序,用剩余时间少的军队去距离近的子树,只要保证其他子树至少有一个就好了!
    
        很对很对恩。。    
    
    码码码。。。。
    
    I:Wocao,60!怎么回事⊙ o ⊙ 。
    

    如果您也在纳闷,那么不妨看这样一组数据:

    Input:
    4
    1 2 9
    1 3 1
    1 4 2
    3 
    1 1 2
    Output:
    10
    

    如果您犯了这个错误,应该已经明白问题在哪了。

    所以说这个地方还是很容易成为一个思维漏洞的,这应该也是此题最关键的地方了。

    这个时候我们需要遵循一个贪心策略 :

    如果一些满足上述条件的军队Ai(都在S处),其中最小的不能满足他剩余的距离能够到root又回来,即小于2*dis(root,S);
    那么必须留下那个剩余时间最短的军队守S,其余的当做跨根的用,否则可以都当做跨跟的用。
    

    至此,我们就可以开心的解决这个问题了,代码实现过程中可能需要注意一下下吧。

    Code↓:

    // luogu-judger-enable-o2
    #include<queue>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define RG register
    #define IL inline
    #define LL long long
    #define DB double
    using namespace std;
    
    IL int gi() {
        char ch=getchar(); RG int x=0,w=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=1e5+10;
    
    queue<int> q;
    vector<LL> s[N];
    LL L,R=1e15,mid,d[N][21];
    int n,m,Lef,num,cnt,PaPa,tot,head[N],army[N],f[N][21],vis[N],tag[N],leaf[N],Pa[N],occ[N];
    
    struct ARMY{int bel;LL res;}A[N],Need[N];
    struct EDGE{int next,to,v;}e[N<<1];
    
    IL bool CMP(ARMY a,ARMY b) {return a.res<b.res;}
    
    IL void make(int a,int b,int c) {
        e[++tot]=(EDGE){head[a],b,c},head[a]=tot;
        e[++tot]=(EDGE){head[b],a,c},head[b]=tot;
    }
    
    void dfs(int x,int fx) {
        RG int i,y,fl=0;
        for (i=head[x];i;i=e[i].next)
            if ((y=e[i].to)!=fx) {
                if (x==1) PaPa=y;
                fl=1,f[y][0]=x,d[y][0]=e[i].v,dfs(y,x);
            }
        if (!fl) leaf[++Lef]=x,Pa[Lef]=PaPa;
    }
    
    IL void work() {
        RG int i,j,p;
        for (i=1;i<=17;++i)
            for (j=1;j<=n;++j)
                p=f[j][i-1],f[j][i]=f[p][i-1],d[j][i]=d[j][i-1]+d[p][i-1];
    }
    
    IL void BFS(int S) {
        RG int i,x,y;
        q.push(S);
        while (!q.empty()) {
            x=q.front(),q.pop();
            for (i=head[x],vis[x]=1;i;i=e[i].next)
                if ((y=e[i].to)!=f[x][0]&&!vis[y]) q.push(y); 
        }
    }
     
    IL int check() {
        RG LL sum;
        RG int i,j,x;
        memset(occ,0,sizeof(occ));
        memset(vis,0,sizeof(vis));
        memset(tag,0,sizeof(tag));
        for (i=head[1];i;i=e[i].next) s[e[i].to].clear();
        for (i=1,cnt=0,num=0;i<=m;++i) {
            for (j=17,x=army[i],sum=0;j>=0;--j)
                if (f[x][j]>1&&sum+d[x][j]<=mid) sum+=d[x][j],x=f[x][j];
            if (f[x][0]==1) s[x].push_back(mid-sum);
            else if (!vis[x]) BFS(x);
        }
        for (i=head[1];i;i=e[i].next) sort(s[e[i].to].begin(),s[e[i].to].end());	
        for (i=1;i<=Lef;++i)
            if (!vis[leaf[i]]&&!occ[x=Pa[i]]) {
                occ[x]=1;
                if (!s[x].size()||s[x][0]>=2*d[x][0]) Need[++num]=(ARMY){x,d[x][0]};
                else tag[x]=1;
            }
        for (i=head[1];i;i=e[i].next)
            for (x=e[i].to,j=tag[x];j<s[x].size();++j)
                if (s[x][j]>d[x][0]) A[++cnt]=(ARMY){x,s[x][j]-d[x][0]};
        sort(A+1,A+cnt+1,CMP);	
        sort(Need+1,Need+num+1,CMP);
        for (i=1,j=1;i<=num;++i,++j) {
            while (j<=cnt&&Need[i].res>A[j].res) ++j;
            if (j==cnt+1) break;
        }
        return i>num;	
    }	
    
    int main()
    {
        RG int i,x,y,z;
        for (i=1,n=gi();i<n;++i) x=gi(),y=gi(),z=gi(),make(x,y,z);
        for (i=1,m=gi();i<=m;++i) army[i]=gi();
        dfs(1,1),work();
        while (L<R) {
            mid=L+R>>1;
            if (check()) R=mid;
            else L=mid+1;
        }
        printf("%lld
    ",R);
        return 0;
    }
    
    // 程序有点慢
    // 一定要注意有一个很重要的点:
    // 那就是如果只有一个军队A驻扎在S,S∈SON(root),那么有两种可能性
    // 一另是A守住S这一棵子树,另一种是A去别的子树S',别的A'来S.
    // 此时有一个结论需要遵守:
    // 如果一些满足上述条件的军队Ai(都在S处),其中最小的不能满足他剩余的距离能够到root又回来,即小于2*dis(root,S);
    // 那么必须留下那个剩余时间最短的军队守S,其余的当做跨根的用.
        
    

    The End

  • 相关阅读:
    JSON
    vue中跳转页面逻辑
    生命周期的几个阶段
    vue学习大纲
    Vue留言 checked框案列
    Vue内容
    linux -- iptables
    linux --- DNS
    linux --- samba
    linux --- vsftp
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10617293.html
Copyright © 2020-2023  润新知