LOJ6045 价

人类智慧之神zhangzj最近有点胖,所以要减肥,他买了\(N\)种减肥药,发现每种减肥药使用了若干种药材,总共正好有\(N\)种不同的药材。

经过他的人脑实验,他发现如果他吃下去了\(K~(0\leq K\leq N)\)种减肥药,而这\(K\)种减肥药使用的药材并集大小也为\(K\),这\(K\)种才会有效果,否则无效。

\(i\)种减肥药在产生效果的时候会使zhangzj的体重增加\(P_i\)斤,显然\(P_i\)可以小于\(0\)

他想知道,一次吃药最好情况下体重变化量是多少,当然可以一种药也不吃,此时体重不变。

由于某些奥妙重重的情况,我们可以让这\(N\)种减肥药每一种对应一个其使用的药材,且\(N\)种减肥药对应的药材互不相同(即有完美匹配)。

对于\(100\%\)的数据,\(1 \leq N \leq 300,~|P_i| \leq 1000000\)

题解

把减肥药的权值设成\(-P_i+\inf\),把药材的权值设成\(-\inf\),然后看成最大权闭合子点集中间连\(\infty\)的边,跑个最小割就能得出答案了。

会不会出现选了\(x\)种减肥药,并且对应了\(y~(y>x)\)种药材的情况呢?如果出现了的话,因为\(y\times\inf>x\times(-P_i+\infty)\),所以左侧一定割断了至少一条边。对应最大权闭合子点集的意义,那么左侧一定有至少一种减肥药没有被选,与假设矛盾。所以不存在这种情况,正确性有保证。

CO int64 inf=1e18,mx=1e9;
CO int N=1e3;
namespace Flow{
	int S,T;
	struct edge {int v;int64 c;int a;};
	vector<edge> to[N];
	int dis[N];
	
	IN void init(int n){
		S=n-1,T=n;
	}
	IN void link(int u,int v,int64 c){
		to[u].push_back({v,c}),to[v].push_back({u,0});
		to[u].back().a=to[v].size()-1,to[v].back().a=to[u].size()-1;
	}
	bool bfs(){
		fill(dis+1,dis+T+1,-1),dis[T]=0;
		deque<int> Q={T};
		while(Q.size()){
			int u=Q.front();Q.pop_front();
			for(CO edge&e:to[u])
				if(to[e.v][e.a].c and dis[e.v]==-1){
					dis[e.v]=dis[u]+1;
					Q.push_back(e.v);
				}
		}
		return dis[S]!=-1;
	}
	int64 dfs(int u,int64 lim){
		if(u==T) return lim;
		int64 rest=lim;
		for(edge&e:to[u])
			if(e.c and dis[e.v]==dis[u]-1){
				int delta=dfs(e.v,min(e.c,rest));
				if(!delta) {dis[e.v]=-1; continue;}
				rest-=delta,e.c-=delta,to[e.v][e.a].c+=delta;
				if(!rest) break;
			}
		return lim-rest;
	}
	int64 main(){
		int64 ans=0;
		while(bfs()) ans+=dfs(S,inf);
		return ans;
	}
}

int main(){
	int n=read<int>();
	Flow::init(2*n+2);
	for(int i=1;i<=n;++i)for(int t=read<int>();t--;)
		Flow::link(i,n+read<int>(),inf);
	int64 ans=0;
	for(int i=1;i<=n;++i){
		int64 p=read<int64>();
		Flow::link(Flow::S,i,mx-p);
		ans+=mx-p;
		Flow::link(n+i,Flow::T,mx);
	}
	ans-=Flow::main();
	printf("%lld\n",-ans);
	return 0;
}

posted on 2020-04-27 18:37  autoint  阅读(116)  评论(0编辑  收藏  举报

导航