zoukankan      html  css  js  c++  java
  • bzoj4849: [Neerc2016]Mole Tunnels

    Description

    鼹鼠们在底下开凿了n个洞,由n-1条隧道连接,对于任意的i>1,第i个洞都会和第i/2(取下整)个洞间有一条隧
    道,第i个洞内还有ci个食物能供最多ci只鼹鼠吃。一共有m只鼹鼠,第i只鼹鼠住在第pi个洞内,一天早晨,前k只
    鼹鼠醒来了,而后n-k只鼹鼠均在睡觉,前k只鼹鼠就开始觅食,最终他们都会到达某一个洞,使得所有洞的ci均大
    于等于该洞内醒着的鼹鼠个数,而且要求鼹鼠行动路径总长度最小。现对于所有的1<=k<=m,输出最小的鼹鼠行动
    路径的总长度,保证一定存在某种合法方案。

    Input

    第一行两个数n,m(1<=n,m<=100000),表示有n个洞,m只鼹鼠。
    第二行n个整数ci表示第i个洞的食物数。
    第三行m个整数pi表示第i只鼹鼠所在洞pi。

    Output

    输出一行m个整数,第i个整数表示当k=i时最小的鼹鼠行动路径总长度。

    可以得到一个比较显然的最小费用流建图:

    原有树边流量+inf,费用1,双向

    源点到第k只鼹鼠的位置连边,流量1,费用-k*inf(强制第k次增广时走这条边)

    每个点i到汇点连边,流量ci,费用0

    每次增广一个单位流量可得到所有答案

    考虑优化找最小费用增广路的过程,只需关心增广路在树上的部分

    维护树上从根走到每个点的最小费用(深度),当增广导致反向边出现或消失从而使一条向下的边费用改变时,打标记修改整棵子树的深度,同时维护子树内 到汇点的边还没满流的点 的 最小深度

    起点是已知的,枚举起点和终点可能的lca,查出最优的终点,增广并修改对应边权,更新起点到终点、lca到根的路径上的信息

    标记上下传的具体实现类似于平衡树上的标记

    时间复杂度O(nlogn)

    #include<cstdio>
    const int N=100007,inf=0x3f3f3f3f;
    int n,m;
    char buf[N*50],*ptr=buf;
    int _(){
        int x=0;
        while(*ptr<48)++ptr;
        while(*ptr>47)x=x*10+*ptr++-48;
        return x;
    }
    int ev[N],mx,et[N],ans=0;
    struct node{
        int dep,a,mn;
        void add(int x){
            dep+=x;a+=x;mn+=x;
        }
    }tr[N];
    void mins(int&a,int b){if(a>b)a=b;}
    void up(int w){
        int l=w<<1,r=l^1;
        tr[w].mn=ev[w]?tr[w].dep:inf;
        if(l<=n)mins(tr[w].mn,tr[l].mn);
        if(r<=n)mins(tr[w].mn,tr[r].mn);
    }
    void dn(int w){
        if(!tr[w].a)return;
        int l=w<<1,r=l^1;
        if(l<=n)tr[l].add(tr[w].a);
        if(r<=n)tr[r].add(tr[w].a);
        tr[w].a=0;
    }
    void dfs(int w,bool f){
        dn(w);
        if(ev[w]&&tr[w].mn==tr[w].dep){
            --ev[w];
        }else{
            int u=w<<1^(tr[w].mn!=tr[w<<1].mn);
            dfs(u,1);
        }
        up(w);
        if(f&&!++et[w])tr[w].add(2);
    }
    void dns(int w){
        if(!w)return;
        dns(w>>1);
        dn(w);
    }
    void mcf(int x){
        dns(x);
        int mc=inf,mw,c1=0;
        for(int y=x;y;y>>=1){
            int c2=c1+tr[y].mn-tr[y].dep;
            if(c2<mc){
                mc=c2;
                mw=y;
            }
            c1+=et[y]>0?-1:1;
        }
        for(int y=x;y!=mw;y>>=1){
            up(y);
            if(!et[y]--)tr[y].add(-2);
        }
        dfs(mw,0);
        for(;mw;mw>>=1)up(mw);
        ans+=mc;
    }
    int main(){
        fread(buf,1,sizeof(buf),stdin)[buf]=0;
        n=_();m=_();
        for(int i=1;i<=n;++i)ev[i]=_();
        for(int i=1;i<=n;++i)tr[i].dep=tr[i>>1].dep+1;
        for(int i=n;i;--i)up(i);
        for(int i=0;i<m;++i){
            if(i)putchar(32);
            mcf(_());
            printf("%d",ans);
        }
        return 0;
    }
  • 相关阅读:
    大物—⑦热力学基础1
    组原——③系统总线
    组原——①计算机系统概论
    递归
    字符串
    Linux下安装glibc-2.14,解决“`GLIBC_2.14' not found”问题
    libc.so.6被删除修复ln: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
    正则表达式高级用法(分组与捕获)
    查看Android应用包名、Activity的几个方法
    logcat随笔
  • 原文地址:https://www.cnblogs.com/ccz181078/p/6870358.html
Copyright © 2011-2022 走看看