题解:HDU 6598
Description
Now, Bob is playing an interesting game in which he is a general of a harmonious army. There are (n) soldiers in this army. Each soldier should be in one of the two occupations, Mage or Warrior. There are (m) pairs of soldiers having combination ability. There are three kinds of combination ability. If the two soldiers in a pair are both Warriors, the army power would be increased by (a). If the two soldiers in a pair are both Mages, the army power would be increased by (c). Otherwise the army power would be increased by (b), and (b=a/4+c/3), guaranteed that (4|a) and (3|c). Your task is to output the maximum power Bob can increase by arranging the soldiers' occupations.
Note that the symbol (a|b) means that (a) divides (b), e.g. , (3|12) and (8|24).
(n leq 500, m leq 10^4)
多组数据,保证(sum n leq 5 * 10 ^3, sum m leq 5 * 10^4)
题意:给 (n) 个点染上两种颜色。现在给出 (m) 种形如 ((u, v, a, b, c)) 的关系
如果点 (u, v) 同为颜色1,则获得 (a) 的收益;
如果点 (u, v) 同为颜色2,则获得 (c) 的收益;
如果点 (u, v) 颜色不同,则获得 (b) 的收益。
现在求所有染色方案中可获得的最大收益。
Algorithm
题意与数据范围无处不提示我们:这题网络流。
阅题无数的同学们可能会想到文理分科这道题目(https://www.luogu.com.cn/problem/P4313),
确实,虽然具体处理上有差别,本题与文理分科的模型是很相似的。
首先考虑如何表示「染色」。我们考虑建立两个新点 (s, t) 。
接下来考虑某个特定点 (u) ,
如果将点 (u) 染成颜色1,就建立一条(s o u) 的边;
对应地,如果将点 (u) 染成颜色2,就建立一条 (u o t) 的边。
同一个点不可能被染成两种颜色,因此不应该存在一条 (s o dots o t) 的路径。
当然,如果正面考虑如何建边的话就和暴力没什么两样了。
然而,我们可以考虑将所有边建好之后再删去其中的一些,使得删去的边权值和最小。
换言之,我们只需要求出 (s o t) 的最小割即可。
框架已经放好了,接下来要考虑的就是如何建图了。
特别地,我们考虑某个关系 ((u, v, a, b, c))
建立边 $s o u, s o v, u o t, v o t, u leftrightarrow v $,设各边权值如图。
这里我们建立了一条 (u leftrightarrow v) 的双向边,用以控制两点染上不同颜色获得的收益值。
若点 (u, v) 均与 (t) 联通,则需要删去边 (s o u, s o v),
这时只能获得 (c) 的收益,而不能获得 (a + b) 的收益,那么有 (x_1 + x_2 = a + b)。
同理我们有 (y_1 + y_2 = b + c)。
接下来考虑两点分别染上不同颜色的情况。
先考虑 (u) 与 (s) 联通,(v) 与 (t) 联通的情况,我们此时要删去 (s o v, v o u, u o t) 共三条边,
损失的收益为 (a + c) ,于是有 (x_2 + z + y_1 = a + c) 。
同理我们有 (x_1 + z + y_2 = a + c) 。
综合,我们得到方程组:
考虑到(x_1, x_2) 或 (y_1, y_2) 的地位应当相等,容易得到一组解:
考虑到除以二可能会导致不必要的精度误差,可以全部乘二再建图。
由众所周知的最小割等于最大流,我们在图上跑一边dinic就没事了。
最后的答案是
代码在细节上可能有些小小的不一样,毕竟是考场写的和博客说的肯定不太一样。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
template<typename T>
inline void read(T &x)
{
char c = getchar(); x = 0;
while(c < '0' || '9' < c) c = getchar();
while('0' <= c && c <= '9')
{
x = (x << 1) + (x << 3) + c - 48;
c = getchar();
}
}
typedef long long ll;
const ll INF = 1e18;
template<const int N, const int M>
class Graph {
private:
int beg[N], nex[M], tar[M], len;
int lev[N], ite[N], goa;
ll cap[M];
queue<int> que;
public:
inline void clear()
{
memset(beg, 0, sizeof(beg));
memset(nex, 0, sizeof(nex));
len = 1;
}
Graph() {
clear();
}
inline void add_edge(int a, int b, ll c) {
++len, tar[len] = b, cap[len] = c;
nex[len] = beg[a], beg[a] = len;
}
inline void add_pipe(int a, int b, ll c) {
add_edge(a, b, c), add_edge(b, a, 0);
}
void bfs(int s)
{
memset(lev, -1, sizeof(lev));
lev[s] = 0, que.push(s);
while(!que.empty())
{
int cur = que.front(); que.pop();
for(int i = beg[cur]; i; i = nex[i])
if(cap[i] > 0 && lev[tar[i]] == -1)
{
lev[tar[i]] = lev[cur] + 1;
que.push(tar[i]);
}
}
}
ll dfs(int cur, ll flo)
{
if(cur == goa) return flo;
for(int &i = ite[cur]; i; i = nex[i])
if(cap[i] > 0 && lev[cur] < lev[tar[i]])
{
ll res = dfs(tar[i], min(flo, cap[i]));
if(res)
{
cap[i] -= res;
cap[i ^ 1] += res;
return res;
}
}
return 0;
}
ll dinic(int s, int t)
{
ll res, ans = 0;
goa = t;
while(true)
{
bfs(s);
if(lev[t] == -1) return ans;
memcpy(ite, beg, sizeof(beg));
while((res = dfs(s, INF)) > 0)
ans += res;
}
}
};
Graph<1024, 524288> G;
int n, m, s, t;
int main()
{
while(cin >> n >> m)
{
G.clear();
s = n + n + 1, t = n + n + 2;
ll sum = 0;
int u, v, a, b, c;
for(int i = 0; i != m; ++i)
{
read(u), read(v), read(a), read(b), read(c);
sum += (a + b + c) * 2;
ll x = a + b, y = b + c, z = a + c - 2 * b;
G.add_pipe(s, u, x), G.add_pipe(s, v, x);
G.add_pipe(u, t, y), G.add_pipe(v, t, y);
G.add_pipe(u, v, z), G.add_pipe(v, u, z);
}
printf("%lld
", (sum - G.dinic(s, t)) / 2);
}
return 0;
}