[CF311E]Biologist

luogu

题意

\(n\)\(01\)变量,你要确定它们的值,改变第\(i\)个变量的代价是\(v_i\)
\(m\)个限制条件,每个形如:某个变量集合内的所有元素都必须是\(0\),或者都必须是\(1\)。达成这个条件会获得\(W_i\)的收益,否则有可能会付出\(g\)的代价。
求最大收益。

sol

最大权闭合子图。转化成总收益减去最小损失来做。
最小割。假设\(S\)这边的都是\(0\)\(T\)这边的都是\(1\)
\(n\)个变量\(n\)个点,然后每个限制条件建一个点。
对于一个原本是\(0\)的变量,连边\((S,i,v_i)\),否则连边\((i,T,v_i)\)
对于一个限制,如果要求全都必须是\(0\),那么也就是说只要有一个\(1\)就算作不满足。集合内的所有元素连边\((i+n,x,inf)\),然后连边\((S,i+n,W_i)\)。这样子只要存在一个变量取了\(1\),这个点一定会被割在\(T\)这边,然后\(W_i\)就被算进割集里去了。
注意上述的边都是单向边。也就是说,不能存在从\(S\)跨越到\(T\)\(inf\)边,但是从\(T\)跨越到\(S\)\(inf\)边则是允许的。
要求全部是\(1\)同理,对每个元素连边\((x,i+n,inf)\),然后连边\((i+n,T,W_i)\)即可。
\(g\)的损失直接加到\(W_i\)里面去就好了。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 15000;
const int inf = 1e9;
struct edge{int to,nxt,w;}a[N*10];
int n,m,g,sex[N],S,T,head[N],cnt=1,dep[N],cur[N],ans;
queue<int>Q;
void link(int u,int v,int w)
{
	a[++cnt]=(edge){v,head[u],w};
	head[u]=cnt;
	a[++cnt]=(edge){u,head[v],0};
	head[v]=cnt;
}
bool bfs()
{
	memset(dep,0,sizeof(dep));
	dep[S]=1;Q.push(S);
	while (!Q.empty())
	{
		int u=Q.front();Q.pop();
		for (int e=head[u];e;e=a[e].nxt)
			if (a[e].w&&!dep[a[e].to])
				dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
	}
	return dep[T];
}
int dfs(int u,int f)
{
	if (u==T) return f;
	for (int &e=cur[u];e;e=a[e].nxt)
		if (a[e].w&&dep[a[e].to]==dep[u]+1)
		{
			int tmp=dfs(a[e].to,min(a[e].w,f));
			if (tmp) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
		}
	return 0;
}
int Dinic()
{
	int res=0;
	while (bfs())
	{
		for (int i=1;i<=T;++i) cur[i]=head[i];
		while (int tmp=dfs(S,inf)) res+=tmp;
	}
	return res;
}
int main()
{
	n=gi();m=gi();g=gi();S=n+m+1;T=S+1;
	for (int i=1;i<=n;++i) sex[i]=gi();
	for (int i=1;i<=n;++i)
	{
		int x=gi();
		sex[i]?link(i,T,x):link(S,i,x);
	}
	for (int i=1;i<=m;++i)
	{
		int v=gi(),w=gi(),k=gi();ans+=w;
		while (k--)
		{
			int x=gi();
			v?link(x,i+n,inf):link(i+n,x,inf);
		}
		int gg=gi();w+=g*gg;
		v?link(i+n,T,w):link(S,i+n,w);
	}
	printf("%d\n",ans-Dinic());
	return 0;
}
posted @ 2018-04-04 16:37  租酥雨  阅读(986)  评论(0编辑  收藏  举报