CF311E Biologist(最小割)

题目链接

CF:https://codeforc.es/contest/311/problem/E

luogu:https://www.luogu.com.cn/problem/CF311E

首先很套路地,我们可以先把所有的w[i]加起来,再考虑最小化代价

即Ans = Σw[i] - Mincut

我们可以把原来的字符串的点当作i(1 <= i <= n),m个要求也分别当作点i + n(1<= i <= m)

首先我们考虑一下限制条件:

1.如果不选这个要求,我们会损失w[i] 或 w[i] + g 设为f[i]

2.如果选了这个要求,以这个要求1为例,设这个要求需要为1的若干个位置为S,设原字符串数组为str[i],则满足i属于集合S且str[i] == 0的i都要改变,即付出v[i]的代价

3.相互矛盾的两个要求不能同时选,即某个要求需要位置i为0,另一个需要位置i为1,则这两个位置不能同时选

建模

1.我们考虑把str[i] == 1的点与S连边,流量为v[i],意即如果要变成0需要付出v[i]的代价,str[i] == 0则向T连边

2.我们考虑把要求1的点向S连边,流量为f[i],然后向它要求的点连边,流量为inf,为么

要这样连呢,因为这样连后我们会发现要么要把它向S的边割掉损失f[i],要么要把那些需要花代价变成1的点与T的连边割掉.意即要付出变成1需要花费的代价.要求0的点同理

然后我们就满足了要求1,2,然后交上去就AC了.

??????为什么???我们不是还没满足限制3吗?数据水????

这个地方是博主觉得这题最妙的地方,我们这样连边其实是满足限制3的

为什么呢,放一张图相信大家都能看懂

从图中可以看出要求1和要求2通过同一个点x形成了一条从S到T联通的路径,由于最小割要割断S和T的路径所以互相矛盾的要求不可能同时选

最优性可以由最小割算法本身保证

所以就做完这道题啦

代码如下

/*CF311E Biologist*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
const int maxn = 1e6 + 10;
struct Edge{
	int nxt,point,flow;
}edge[maxn<<1];
int S,T,dep[maxn],cur[maxn],head[maxn],q[maxn],tot,n,m;
void add(int x,int y,int flow){
	edge[++tot].nxt = head[x];edge[tot].point = y;edge[tot].flow = flow;head[x] = tot;
	edge[++tot].nxt = head[y];edge[tot].point = x;edge[tot].flow = 0;	head[y] = tot;
}
bool bfs(){
	for(int i = S; i <= T; ++i)		cur[i] = head[i],dep[i] = 0;
	dep[S] = 1;
	int l = 1,r = 0;
	q[++r] = S;
	while(l <= r){
		int x = q[l++];
		for(int i = head[x]; i ; i = edge[i].nxt){
			int y = edge[i].point;
			if(!dep[y] && edge[i].flow){
				dep[y] = dep[x] + 1;
				q[++r] = y;
			}
		}
	}
	return (dep[T] != 0);
}
int Dinic(int x,int flow){
	if(x == T || !flow)	return flow;
	int rest = flow,k;
	for(int i = cur[x]; i ; i = edge[i].nxt){
		int y = edge[i].point;
		cur[x] = i;
		if(edge[i].flow && dep[y] == dep[x] + 1){
			k = Dinic(y,min(edge[i].flow,rest));
			if(!k)	dep[y] = 0;
			rest -= k;
			edge[i].flow -= k;
			edge[i^1].flow += k;
		}
	}
	return flow - rest;
}
int v[maxn];
int str[maxn];
int main(){
	tot = 1;
	n = read(),m = read();int g = read();
	T = n + m + 1;
	for(int i = 1; i <= n; ++i)		str[i] = read();
	for(int i = 1; i <= n; ++i){
		v[i] = read();
		if(str[i] == 1)		add(S,i,v[i]);
		else	add(i,T,v[i]);
	}
	ll ans = 0;
	for(int i = 1; i <= m; ++i){
		int opt = read();int w = read(),k = read();
		ans += w;
		for(int j = 1; j <= k; ++j){
			int x = read();
			if(opt)		add(n+i,x,1e9);
			else	add(x,n+i,1e9);
		} 
		int t = read();
		if(opt)		add(S,n+i,w+g*t);
		else	add(n+i,T,w+g*t);
	} 
	while(bfs())	ans -= Dinic(S,1e9);
	cout<<ans<<endl;
	return 0;
}

  

 

posted @ 2020-09-29 17:29  y_dove  阅读(305)  评论(0编辑  收藏  举报