CF311E思路分享(网络流,最大权闭合子图)

https://codeforces.com/contest/311/problem/E

题意概述

给定 \(n\) 只狗,初始性别为 \(0\)\(1\),可以花费 \(v_i\) 元改变第 \(i\) 只狗的性别。

\(m\) 个富人,第 \(i\) 个富人会指定 \(k_i\) 只特定的狗和一个性别,如果指定的所有狗的性别与指定的性别一致,获得 \(w_i\) 元;如果该富人是你的朋友,且你没有满足他的要求,需要支付给他固定 \(g\) 元。

求最大净收益(可能为负)。

思路

为了满足某些要求以获得利润,可能需要花费代价,存在依赖关系且要求最大化利润,符合最大权闭合子图模型。

最大权闭合子图

最大权闭合子图:在一个有向图中,每个点有点权,选择权值和最大的一个子图,并且满足子图中每个点的所有后继都在子图中,这个子图就是最大权闭合子图。

求解最大权的方法:虚拟出源点 \(s\) 和汇点 \(t\),从 \(s\) 向原图中所有点权为正的点连边,容量为点权;从原图中所有点权为负的点向 \(t\) 连边,容量为点权的绝对值;对于原图中的边,容量为无穷大。最终答案为原图中的所有正点权之和 \(-\) 最小割。

解释如下:

首先规定分到 \(S\) 集的点作为被选中作为子图中的点。

考虑割的意义:如果割掉 \(s\)\(i\) 的边,那么 \(i\) 不在 \(S\) 集中,相当于不选 \(i\) 作为子图中的点;如果割掉 \(i\)\(t\) 的边,那么 \(i\) 不在 \(T\) 集中,相当于选 \(i\) 作为子图中的点。

因此,割的容量为:不选的正点权之和 \(+\) 选择的负点权的绝对值之和。

而子图的权值和为:所有的正点权之和 \(-\) 不选的正点权之和 \(-\) 选择的负点权的绝对值之和,也就是:所有正点权之和 \(-\) 割的容量。

因此只需要最小化割的容量即可。同时由于原图的边容量为无穷大,在最小割中不可能被割掉,选中的子图满足原图的依赖关系。


回到本题。

为了方便处理,先将所有点的性别都改成 \(1\),此时的收益为:要求性别为 \(1\)\(w_i\) 之和 \(-\) 初始性别为 \(0\) 的点的 \(v_i\) 之和 \(-\) 要求性别为 \(0\) 且是朋友的要求数量 \(\cdot g\)

对于每个点,规定选中某点代表改变该点的性别。如果原本性别为 \(0\),改变性别相当于反悔,贡献为 \(v_i\);如果原本性别为 \(1\),贡献为 \(-v_i\)

对于每个要求,虚拟成新点。

如果要求性别为 \(0\),选择该要求需要将所有关联点性别都改成 \(0\),于是从该虚拟点向所有关联点连边。如果不是朋友,贡献为 \(w_i\);否则为 \(w_i + g\)

如果要求性别为 \(1\),选择该要求需要所有关联点中任意一个被修改成 \(0\),于是从所有关联点向该虚拟点连边。如果不是朋友,贡献为 \(-w_i\),否则为 \(- w_i - g\)

将贡献作为点权,求出最大权闭合子图即可。


//author:kzssCCC

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

const ll INF = 9e18;

class Q{
public:
	int t,f,k;
	ll w;
	vector<int> a;

	Q() = default;	
	Q(int t,int f,int k,ll w,vector<int>& a):t(t),f(f),k(k),w(w),a(a){}
};

void solve(){
	int n,m,g;
	cin >> n >> m >> g;

	vector<int> sex(n+1);
	vector<ll> V(n+1);

	for (int i=1;i<=n;i++){
		cin >> sex[i];
	}

	for (int i=1;i<=n;i++){
		cin >> V[i];
	}

	vector<Q> que(m+1);
	for (int i=1;i<=m;i++){
		int t,k;
		ll w;
		cin >> t >> w >> k;

		vector<int> a(k+1);
		for (int j=1;j<=k;j++){
			cin >> a[j];
		}

		int f;
		cin >> f;

		que[i] = {t,f,k,w,a};
	}

	ll res = 0;
	for (int i=1;i<=n;i++){
		if (sex[i]==0){
			res -= V[i];
		}
	}

	for (int i=1;i<=m;i++){
		if (que[i].t==1){
			res += que[i].w;
		}

		if (que[i].t==0 && que[i].f==1){
			res -= g;
		}
	}

	ll sum = 0;
	vector<vector<array<ll,3>>> adj(n+m+3);
	int s=n+m+1,t=n+m+2;

	auto add = [&](int u,int v,ll w){
		adj[u].push_back({w,(int)adj[v].size(),v});
		adj[v].push_back({0,(int)adj[u].size()-1,u});
	};

	for (int i=1;i<=n;i++){
		if (sex[i]==0){
			add(s,i,V[i]);
			sum += V[i];
		}
		else{
			add(i,t,V[i]);	
		}
	}

	for (int i=1;i<=m;i++){
		if (que[i].t==0){
			add(s,i+n,que[i].w+(que[i].f?g:0));
			sum += que[i].w+(que[i].f?g:0);

			for (int j=1;j<=que[i].k;j++){
				add(i+n,que[i].a[j],INF);
			}
		}
		else{
			add(i+n,t,que[i].w+(que[i].f?g:0));

			for (int j=1;j<=que[i].k;j++){
				add(que[i].a[j],i+n,INF);
			}
		}
	}


	vector<int> cur,depth;

	auto bfs = [&](){
		depth = vector<int>(n+m+3,-1);
		queue<int> q;

		depth[s] = 0;
		q.push(s);

		while (!q.empty()){
			int u = q.front();
			q.pop();

			for (auto& [w,rev,v]:adj[u]){
				if (w>0 && depth[v]==-1){
					depth[v] = depth[u]+1;
					q.push(v);

					if (v==t) return true;
				}
			}
		}

		return false;
	};

	function<ll(int,ll)> dfs = [&](int u,ll mf){
		if (u==t) return mf;

		ll curf = 0;
		int len = adj[u].size();

		for (int& i=cur[u];i<len;i++){
			auto& [w,rev,v] = adj[u][i];

			if (w==0 || depth[v]!=depth[u]+1) continue;

			ll f = dfs(v,min(mf,w));
			curf += f;
			adj[v][rev][0] += f;

			mf -= f;
			w -= f;

			if (mf==0) break;
		}

		if (curf==0){
			depth[u] = -1;
		}

		return curf;
	};

	ll mxf = 0;

	while (bfs()){
		cur = vector<int>(n+m+3);
		mxf += dfs(s,INF);
	}

	cout << res+sum-mxf << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-07 18:19  kzssCCC  阅读(11)  评论(0)    收藏  举报