【BZOJ5482】tree(模拟费用流)

传送门


解析:

虽然说确实是一个很裸的费用流,但是开始真没往模拟费用流上面想。

然后发现有负环了,打了 D i j k s t r a Dijkstra Dijkstra加势,不知道哪里错了就咕咕咕了。

知道是模拟费用流之后感觉还挺傻逼的。

由于我们确定是一棵完全二叉树了,那么树高就是 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn)

这么优秀的结构就没必要用裸的费用流跑了。

我们发现,实际上树边处是不限制流量的。

维护一下每个点向子树内跑到一个空位的最小费用,维护每条树边的当前流量。

然后就能愉快的进行模拟费用流操作了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
} 
using namespace IO;

using std::cout;
using std::cerr;

cs int N=3e5+5;
int n,m;
int c[N],flow[N],son[N],down[N];

inline void pushup(int u){
	son[u]=2;
	down[u]=c[u]?0:0x3f3f3f3f;
	for(int re i=0,v;i<2;++i){
		v=u<<1|i;
		if(v<=n){
			int cur=down[v]+(flow[v]>=0?1:-1);
			if(cur<down[u]){
				down[u]=cur;
				son[u]=i;
			}
		}
	}
}

inline void augment(int u){
	if(c[u]){
		--c[u];
	}
	else {
		int v=u<<1|son[u];
		++flow[v];
		augment(v);
	}
	pushup(u);
}

ll ans;
signed main(){
	n=getint(),m=getint();
	for(int re i=1;i<=n;++i)c[i]=getint();
	for(int re i=n;i;--i)pushup(i);
	while(m--){
		int u=getint(),anspos=-1,mn=0x3f3f3f3f,add=0;
		for(int re v=u;v;v>>=1){
			if(down[v]+add<=mn){
				anspos=v;
				mn=down[v]+add;
			}
			add+=flow[v]>0?-1:1;
		}
		cout<<(ans+=mn)<<" ";
		for(int re v=u;v>anspos;v>>=1)--flow[v],pushup(v);
		augment(anspos);
		for(int re v=anspos;v;v>>=1)pushup(v);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值