https://www.luogu.com.cn/problem/P6122
NB模拟费用流题
费用流的建图不难,我们考虑如何模拟这个过程
可以暴力模拟这个过程因为树的高度只有\(log n\)所以可以暴力去找增广路,然后暴力增广
维护一下每个点儿子中最近的关键点即可
然后记录一下每条边的流量和方向
code:
#include<bits/stdc++.h>
#define N 400050
using namespace std;
int flow[N], dis[N], pos[N], a[N], n, m;
int UP(int u) {
return flow[u] < 0 ? - 1 : 1;
}
int DOWN(int u) {
return flow[u] > 0 ? - 1 : 1;
}
#define ls (rt << 1)
#define rs (rt << 1 | 1)
const int inf = 1e9;
void update(int rt) {
pos[rt] = 0, dis[rt] = inf;
if(a[rt]) pos[rt] = rt, dis[rt] = 0;
if(dis[rt] > dis[ls] + DOWN(ls)) dis[rt] = dis[ls] + DOWN(ls), pos[rt] = pos[ls];
if(dis[rt] > dis[rs] + DOWN(rs)) dis[rt] = dis[rs] + DOWN(rs), pos[rt] = pos[rs];
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = n + 1; i <= n + n + 1; i ++) dis[i] = inf;
for(int i = n; i >= 1; i --) update(i);
int ans = 0;
while(m --) {
int x = 0, y = 0, z = 0, ret = 1e9, now = 0;
scanf("%d", &x);
for(int u = x; u; u >>= 1) {
if(ret > now + dis[u]) ret = now + dis[u], z = u;
now += UP(u);
}
y = pos[z];
ans += ret;
// printf("%d %d %d %d %d\n", ans, x, y, z, dis[5]);
printf("%d ", ans);
a[y] --;
for(int u = x; u != z; u >>= 1) flow[u] ++;
for(int u = y; u != z; u >>= 1) flow[u] --;
for(int u = y; u != z; u >>= 1) update(u);
for(int u = x; u ; u >>= 1) update(u);
}
return 0;
}