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;
}
posted @ 2020-09-13 19:42  srf  阅读(485)  评论(0编辑  收藏  举报