LOJ#6045. 「雅礼集训 2017 Day8」价 题解
首先把价值取反,然后问题转换为求最大权值。
考虑一个最小割模型:
S连向药,容量INF+权值,割掉这条边相当于不选这个药;
药连向药材,容量INF;
药材连向T,容量INF,割掉这条边相当于选这个药材;
不难发现在最小割方案中,割掉药连向药材的边是不优的。
因为有完美匹配,所以对于任意左部点集合 \(S\) , \(|N(S)| \geq |S|,\) 因此至少会割掉\(n\)条边。
又因为边权加上了 \(INF\) ,所以不会割掉 \(>n\) 条边。
因此不选的药的个数 + 选的药材个数 = n,所以可以得到选的药材个数 = 选的药的个数。
然后跑一个最小割即可。
\(O(Dinic(n,n^2))\)
code :
#include <bits/stdc++.h>
#define LL long long
using namespace std;
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 305,V = N<<1,E = N*N,INF = 600000000;
int To[E<<1],Ne[E<<1],Flow[E<<1],He[V],Now[V],_ = 1;
inline void add(int x,int y,int flow){
++_; To[_] = y,Flow[_] = flow,Ne[_] = He[x],He[x] = _;
++_; To[_] = x,Flow[_] = 0,Ne[_] = He[y],He[y] = _;
}
int n,cntv,S,T;
int dis[V],Q[V],ql,qr;
inline bool Bfs(){
int x,y,p;
memset(dis,0,cntv+1<<2),Q[ql=qr=1] = S,dis[S] = 1;
while (ql <= qr){
x = Q[ql],++ql;
for (p = He[x]; p ; p = Ne[p]) if (Flow[p] && !dis[y=To[p]]) dis[y] = dis[x] + 1,Q[++qr] = y;
}
return dis[T];
}
inline LL Dfs(int x,LL flow){
if (!flow || x == T) return flow;
LL rest = flow; int f,y,p;
for (p = Now[x]; p ; p = Ne[p]){
Now[x] = p; if (Flow[p] && dis[y=To[p]] == dis[x] + 1){
f = Dfs(y,min(rest,(LL)Flow[p])),Flow[p] -= f,rest -= f,Flow[p^1] += f;
if (!rest) return flow;
}
}
dis[x] = -233;
return flow - rest;
}
inline LL Dinic(){
LL sum = 0;
while (Bfs()) memcpy(Now,He,cntv+1<<2),sum += Dfs(S,1ll<<60);
return sum;
}
LL ans;
int main(){
int i,j,k;
read(n),S = n<<1|1,T = S+1,cntv = T;
for (i = 1; i <= n; ++i){ read(k); while (k--) read(j),add(i,j+n,INF); }
for (i = 1; i <= n; ++i) read(j),ans -= j,add(S,i,INF-j),add(i+n,T,INF);
ans -= Dinic() - 1ll * INF * n;
cout << -ans << '\n';
return 0;
}