题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=4849

题解

其实也是模拟费用流,但是这道题和一般的题目不一样,这道题是在一个完全二叉树上
这意味着我们根本不需要考虑什么类似数轴上老鼠进洞之类的做法,我们跑费用流,每次选一条最短路增广即可
然后增广之后最短路上的点费用会由\(1\)变成\(-1\), 直接在完全二叉树上暴力修改暴力维护子树内最近的食物点即可
说白了就是暴力,但是复杂度\(O(n\log n)\).

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iostream>
using namespace std;
 
inline int read()
{
	int x=0; bool f=1; char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
	for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
	if(f) return x;
	return -x;
}

const int N = 1e5;
const int INF = 1e8;
struct Element
{
	int x,pos;
	Element() {}
	Element(int _x,int _pos) {x = _x,pos = _pos;}
};
void update(Element &x,Element y) {if(y.x<x.x) x = y;}
Element dp[N+3];
int w[N+3];
int pos[N+3];
int dep[N+3];
int c[N+3];
int n,m;
 
int LCA(int u,int v)
{
	while(u!=v)
	{
		if(u>v) u>>=1;
		else v>>=1;
	}
	return u;
}

void pushup(int u)
{
	if(w[u]>0) {dp[u] = Element(0,u);}
	else {dp[u] = Element(INF,0);}
	if((u<<1)<=n) {update(dp[u],Element(dp[u<<1].x+(c[u<<1]<0?-1:1),dp[u<<1].pos));}
	if((u<<1|1)<=n) {update(dp[u],Element(dp[u<<1|1].x+(c[u<<1|1]<0?-1:1),dp[u<<1|1].pos));}
//	printf("pushup dp[%d]=(%d,%d)\n",u,dp[u].x,dp[u].pos);
}
 
Element query(int u)
{
	Element ret(INF,0); int tmp = 0;
	while(u)
	{
		update(ret,Element(dp[u].x+tmp,dp[u].pos));
		tmp += c[u]>0?-1:1;
		u>>=1;
	}
	return ret;
}
 
void addval(int u,int v,int x)
{
	while(u!=v)
	{
		c[u] += x;
		pushup(u>>1);
		u>>=1;
	}
	while(u>0)
	{
		pushup(u);
		u>>=1;
	}
}
 
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) scanf("%d",&w[i]);
	for(int i=1; i<=m; i++) scanf("%d",&pos[i]);
	dep[1] = 1; for(int i=2; i<=n; i++) dep[i] = dep[i>>1]+1;
	for(int i=n; i>=1; i--) pushup(i);
	int ans = 0;
	for(int i=1; i<=m; i++)
	{
		int u = pos[i];
		Element tmp = query(u);
		ans += tmp.x; int v = tmp.pos,lca = LCA(u,v);
		w[v]--; pushup(v);
		addval(u,lca,-1);
		addval(v,lca,1);
		printf("%d ",ans);
	}
	return 0;
}