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