CF903G Yet Another Maxflow Problem 题解

比较好想,但是细节比较多。

我们考虑到最大流 == 最小割,于是我们直接刻画最小割即可。

由于图的特殊性质,左侧 ii+1i \rightarrow i+1 和右侧 ii+1i \rightarrow i+1 在最小割必然至多只会割一次。

我们假设左侧选的是割 ii+1i \rightarrow i+1 的边,右侧是 jj+1j \rightarrow j+1 的边,aia_i 是左侧这条边容量,bib_i 是右侧这条边容量。则 (i,j)(i,j) 的结果是 ai+bj+(x,y)(x,y)Exiy>jcx,ya_i+b_j+\sum \limits_{(x,y)|(x,y)\in E \land x \leq i \land y>j} c_{x,y}

又注意到,我们修改的仅仅是左侧 ii+1i \rightarrow i+1 的边权,与中间和右侧无关,所以我们可以对于每个 ii 求出相对应的最小的 jj,这显然可以从前往后使用线段树维护区间加全局 min\min

修改时只需要修改 aia_i 并维护全局 min\min 即可。

注意上述讨论都基于两边都割一条边,但事实上可以选择不割两边只割中间,割一边不割另一边。讨论一下即可。

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2e5 + 5;
using ll = long long;

int n, m, q;
ll a[N], b[N];

ll res[N];
vector<pair<int, ll>> v[N], vv[N];

class SegmentTree
{
public:
	struct Node
	{
		int l, r;
		ll minn, tag;
	}tr[N << 2];
	void pushup(int u)
	{
		tr[u].minn = min(tr[u << 1].minn, tr[u << 1 | 1].minn);
	}
	void pushtag(int u, ll t)
	{
		tr[u].tag += t;
		tr[u].minn += t;
	}
	void pushdown(int u)
	{
		if (tr[u].tag)
		{
			pushtag(u << 1, tr[u].tag);
			pushtag(u << 1 | 1, tr[u].tag);
			tr[u].tag = 0;
		}
	}
	void build(int u, int l, int r, ll *b)
	{
		tr[u] = { l, r, b[l], 0LL };
		if (l == r) return;
		int mid = l + r >> 1;
		build(u << 1, l, mid, b);
		build(u << 1 | 1, mid + 1, r, b);
		pushup(u);
	}
	void update(int u, int l, int r, ll v)
	{
		if (tr[u].l >= l and tr[u].r <= r)
		{
			pushtag(u, v);
			return;
		}
		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid) update(u << 1, l, r, v);
		if (r > mid) update(u << 1 | 1, l, r, v);
		pushup(u);
	} 
}sgt, s2;

ll s[N];

signed main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m >> q;
	for (int i = 2; i <= n; i++)
	{
		cin >> a[i] >> b[i]; 
	}
	bool f = 1;
	ll ss2 = 0;
	ll ss = 0;
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		ll z;
		cin >> x >> y >> z;
		if (y == n) ss += z; 
		else f = 0;
		ss2 += z;
		v[x].emplace_back(make_pair(y, z));
		vv[y].emplace_back(make_pair(x, z));
	}
	b[1] = (int)1e15;
	sgt.build(1, 1, n, b);
	for (int i = 2; i <= n; i++)
	{
		ll tot = 0; 
		for (auto &[j, v] : ::v[i - 1])
		{
			sgt.update(1, 1, j, v);
			tot += v;
		}
		res[i] = sgt.tr[1].minn;
		res[i] += a[i];
		s[i] = s[i - 1] + tot;
		res[i] = min(res[i], a[i] + s[i]);
	}
	res[1] = (int)1e15;
	s2.build(1, 1, n, res);
	ll ress = ss + (!f ? b[n] : 0LL);
	ll sus = 0;
	for (int i = n - 1; i >= 2; i--)
	{
		ll sp = ss + b[i];
		for (auto &[k, v] : vv[i]) sus += v; 
		ress = min(ress, sp + sus);
	}
	cout << min(s2.tr[1].minn, min(ress, ss2)) << "\n";
	while (q--)
	{
		int x, y;
		cin >> x >> y;
		x++;
		s2.update(1, x, x, -a[x]);
		a[x] = y;
		s2.update(1, x, x, a[x]);
		cout << min(s2.tr[1].minn, min(ress, ss2)) << "\n";	 
	}
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示