题目链接

最小割

思路:总的权值和 - 最小割 = 最大权值

建图:
对于边(u, v),从源点s向u建一条边,从u向汇点t建一条边,两条边分别表示点u的两个职位,v同理。u和v互相建一条双向边。根据上面的思路,对于每一条边的取值,我们要满足边的权值和减去最小割是我们要求的最大权值。

分别给边编号
a: (s, u), b: (s, v), c: (u, t), d:(v, t), e: (u, v),(v, u)。

权值和sum = A+B+C, A是同为Warriors的权值,B是不同职位的权值,C是同为Mages的权值。
假设只存在一条关系边 (u, v),根据上面的方法建图,可能存在的最小割是
(a+b), (c+d), (a+e+d), (b+e+c)
只要边权的取值满足:
a+b = B+C (sum-(B+C) = A, 同为Warriors)
c+d = A+B (sum-(A+B) = C, 同为Mages)
a+e+d = A+C (sum-(A+C) = B, 不同职位)
b+e+c = A+C (sum-(A+C) = B, 不同职位)

解方程后得出 a = b = (B+C)/2, c = d = (A+B)/2, e = -B+(A+C)/2。
因为题目给出 3|c, 所以权值赋值的时候都要同乘一个2,输出答案的时候再除回来。

#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>

using namespace std;

typedef long long ll;

const int MaxnN = 500+10;
const int MaxnE = 1e5+10;
const int INF = 0x3f3f3f3f;
const long long LINF = 1e18;

struct Edge {
	int v, next;
	ll cap, flow;
} edge[MaxnE<<1];

int h[MaxnN], edge_cnt;
int n, m;
int deep[MaxnN], cur[MaxnN];
 
inline void add(int u, int v, int c) {
 	edge[edge_cnt].v = v;
 	edge[edge_cnt].cap = c;
 	edge[edge_cnt].flow = 0;
 	edge[edge_cnt].next = h[u];
 	h[u] = edge_cnt++;
 	
 	edge[edge_cnt].v = u;
 	edge[edge_cnt].cap = 0;
 	edge[edge_cnt].flow = 0;
 	edge[edge_cnt].next = h[v];
 	h[v] = edge_cnt++;
}
 
 
inline bool bfs(int s, int t) {
 	for(int i = s; i <= t; ++i) deep[i] = 0;
 	deep[s] = 1;
 	queue <int> qu;
 	qu.push(s);
 	
 	while(!qu.empty()) {
 		int u = qu.front(); qu.pop();
 		
		for(int i = h[u]; i != -2; i = edge[i].next) {
			Edge e = edge[i];
			if(deep[e.v] == 0 && e.cap > e.flow) {
				deep[e.v] = deep[u]+1;
				qu.push(e.v);
			}
		}	
	}
	return deep[t] > 0;
}
 
inline ll dfs(int u, ll a, int t) {
 	if(u == t || a == 0) return a;
 	ll flow = 0, f;
 	if(cur[u] == -1) cur[u] = h[u];
 	for(int &i = cur[u]; i != -2; i = edge[i].next) {
 		Edge &e = edge[i];
 		if(deep[e.v] == deep[u]+1 && (f = dfs(e.v, min(a, e.cap-e.flow), t)) > 0) {
 			e.flow += f;
			edge[i^1].flow -= f;
			flow += f;
			a -= f;
			if(a == 0) break;	
		}
	}
	return flow;
}
 
inline ll max_flow(int s, int t) {
 	ll flow = 0;
 	while(bfs(s, t)) {
 		for(int i = s; i <= t; ++i) cur[i] = -1;
 		flow += dfs(s, LINF, t);
	}
	return flow;
}
 
int main(void)	
{
 	while(scanf("%d%d", &n, &m) != EOF) {
 		for(int i = 0; i <= n+1; ++i) h[i] = -2;
 		edge_cnt = 0;
 		
 		int s = 0, t = n+1, u, v, aa, bb, cc;
 		ll ans = 0;
 		for(int i = 0; i < m; ++i) {
 			scanf("%d%d%d%d%d", &u, &v, &aa, &bb, &cc);
 			aa *= 2; bb *= 2; cc *= 2; 
 			add(s, u, (aa+bb)/2);
 			add(s, v, (aa+bb)/2);
 			add(u, t, (cc+bb)/2);
 			add(v, t, (cc+bb)/2);
 			add(u, v, (-bb+(aa+cc)/2));
 			add(v, u, (-bb+(aa+cc)/2));
 			ans += aa+bb+cc;
		}
		ans = ans-max_flow(s, t);
		printf("%lld\n", ans/2);
	}
 	return 0;
}