ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

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;
}

 

posted on 2017-05-18 01:27  nul  阅读(757)  评论(0编辑  收藏  举报