题目大意
先放链接
简述一下:
有n个布尔变量,开始时分别是0或1。你可以改变这些变量,改变(0变1,1变0)第i个变量的代价是v[i]
现在有m个限制,形如a,b,x1,x2,x3…,xk,表示希望x1-xk都是a(a为0或1),都满足后可以获得b的价值
有一些限制如果没有满足要倒扣g的价值,g对于所有限制是一个相同的值
求最后能获得的最大价值
思路
一眼网络流
一个错误的思路:假设我们只考虑1,那么每个人到s连边,每个人到ta希望是1的连边,每个1(如果要改)向t连边。总和减最小割。但是这样无法考虑1和0。考虑使得1和0不能同时选(也不能同时不选),于是不能都不割,也不能都割,所以把是1和是0作为两个点,使他们在一个路径上。这样无法表示不满足某个限制。
想不粗来了qwq
于是注意到我们有一些条件没有用:每个限制一定是某些全是1/全是0,于是其实限制是两类
那么把变量放中间,希望是1的限制放在上面连s,希望是0的放下面连t,每个和他所限制的变量连边,这样可以保证每个点所构成的通路一定是一边(上边/下边)都被割了,这样可以表示最终是1/0
现在最后考虑改变变量的代价:假设这个变量最开始是1,那么我们希望这个代价是加在他不是1上的,就是加在他割去表示1那边的时候。那么我们让他割去1之后仍然联通,于是把他跟1那边的虚拟点联通(1的话就是s)。
于是我们解决了这个问题
总结
此题解法可以类比如何使得两个东西不能同时选,不能同时不选
注意观察题目是否有隐藏性质没有使用
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <math.h>
#include <algorithm>
#include <queue>
#include <vector>
#define MAXN 10050
#define INF (1<<28)
using namespace std;
int s, t, n, m, g, sex[MAXN], v[MAXN], ans, level[MAXN<<3], q[MAXN<<3], cnt;
struct node{
int v, c;
node *next, *rev;
}pool[MAXN<<6], *h[MAXN<<4], *cur[MAXN<<4];
inline void addedge(int u, int v, int c){
node *p1 = &pool[cnt++], *p2 = &pool[cnt++];
*p1 = node{v, c, h[u], p2}, h[u] = p1;
*p2 = node{u, 0, h[v], p1}, h[v] = p2;
}
// s 0 t 1
bool bfs(){
int front = 0, rear = 1;
memset(level, 0, sizeof(level));
level[s] = 1;
q[0] = s;
while(front < rear){
int u = q[front++];
if(u == t) return 1;
for(node *p = h[u]; p; p = p->next){
if(!level[p->v] && p->c){
q[rear++] = p->v;
level[p->v] = level[u]+1;
}
}
}
return 0;
}
int dfs(int u, int t, int f){
if(u == t) return f;
int flow = 0, F, Min;
for(node *&p = cur[u]; p; p = p->next){
if(level[p->v] == level[u]+1 && p->c){
Min = min(f-flow, p->c);
F = dfs(p->v, t, Min);
flow += F;
p->c -= F;
p->rev->c += F;
if(flow == f) return flow;
}
}
return flow;
}
int Dinic(){
int ret = 0;
while(bfs()){
for(int i = s; i <= t; i++) cur[i] = h[i];
ret += dfs(s, t, INF);
}
return ret;
}
int main(){
scanf("%d%d%d", &n, &m, &g);
t = n+m+1;
int w, k, exp, fri, cur;
for(int i = 1; i <= n; i++) scanf("%d", &sex[i]);
for(int i = 1; i <= n; i++) scanf("%d", &v[i]);
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &exp, &w, &k);
ans += w;
for(int j = 1; j <= k; j++) {
scanf("%d", &cur);
if(exp) addedge(cur+m, i, INF);
else addedge(i, cur+m, INF);
}
scanf("%d", &fri);
w += g*fri;
if(exp) addedge(i, t, w);
else addedge(s, i, w);
}
for(int i = 1; i <= n; i++) {
if(sex[i]) addedge(i+m, t, v[i]);
else addedge(s, i+m, v[i]);
}
int cut = Dinic();
//cout<<cut<<' ';
ans -= cut;
printf("%d", ans);
return 0;
}