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;
}

浙公网安备 33010602011771号