[BZOJ]4849: [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,输出最小的鼹鼠行动路径的总长度,保证一定存在某种合法方案。

Solution

显然可以直接费用流,但是会TLE。
注意到给出的树是二叉树,费用流增广时走过的边数是 log ⁡ \log log级别的,所以可以模拟费用流,暴力维护路径上的边的情况。具体来说就是维护反向边的数量,以及每个点到子树中的点的最短路,最短路到达的是哪一个点。

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=1061109567;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,c[Maxn],f[Maxn],g[Maxn],up[Maxn],down[Maxn]; 
void dfs(int x)
{
    int lc=x<<1,rc=(x<<1)|1;
    if(c[x])f[x]=0,g[x]=x;
    if(lc<=n)
    {
        dfs(lc);
        if(f[lc]+1<f[x])f[x]=f[lc]+1,g[x]=g[lc];
    }
    if(rc<=n)
    {
        dfs(rc);
        if(f[rc]+1<f[x])f[x]=f[rc]+1,g[x]=g[rc];
    }
}
void update(int x)
{
    int lc=x<<1,rc=(x<<1)|1;
    if(c[x])f[x]=0,g[x]=x;
    else f[x]=inf,g[x]=0;
    if(lc<=n)
    {
        int d;
        if(down[lc])d=-1;else d=1;
        if(f[lc]+d<f[x])f[x]=f[lc]+d,g[x]=g[lc];
    }
    if(rc<=n)
    {
        int d;
        if(down[rc])d=-1;else d=1;
        if(f[rc]+d<f[x])f[x]=f[rc]+d,g[x]=g[rc];
    }
}
int main()
{
    memset(f,63,sizeof(f));
    memset(g,63,sizeof(g));
    n=read(),m=read();
    for(int i=1;i<=n;i++)c[i]=read();
    dfs(1);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int x=read();
        int mn=f[x],w=x,t=x,now=0;
        while(t)
        {
            int u=(t&1)^1;
            if(up[t])now--;else now++;
            t>>=1;
            int tmp=now+f[t];
            if(tmp<mn)mn=tmp,w=t;
        }
        ans+=mn;c[g[w]]--;
        printf("%d ",ans);
        int p;
        p=x;
        while(p!=w)
        {
            if(up[p])up[p]--;
            else down[p]++;
            p>>=1;
        }
        p=g[w];
        while(p!=w)
        {
            if(down[p])down[p]--;
            else up[p]++;
            p>>=1;
        }
        p=x;while(p!=w)update(p),p>>=1;
        p=g[w];while(p!=w)update(p),p>>=1;
        p=w;while(p)update(p),p>>=1;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值