题解 P1646 【[国家集训队]happiness】

Infiltrator

2020-03-14 17:10:41

Solution

[$\Large\color{#FFBBFF}\textit{Tian-Xing's blog}$](https://Tian-Xing.github.io) ------------ # Description [传送门](https://www.luogu.com.cn/problem/P1646) ------------ # Solution 一种列方程的套路。 我们先单独找出两个点的关系来考虑。 假设有$x$和$y$两个同学,向$S$连边代表选理科,向$T$连边代表选文科。设$S$向$x$连的有向边为$a$,$S$向$y$连的有向边为$b$,$x$向$T$连的有向边为$c$,$y$向$T$连的有向边为$d$,$x$和$y$之间连了一条双向边$e$,割掉一条边的流量代表损失了这么多的愉悦值。 设都选理科的愉悦值是$v_1$,都选文科的愉悦值是$v_2$,按照题意可列方程组如下: $$a + b = v_1$$ 这是对应的两个同学都选理科。 $$c + d = v_2$$ 这是对应的两个同学都选文科。 $$a + e + d = v_1 + v_2$$ 这是对应的$x$选理科,$y$选文科。 $$b + e + c = v_1 + v_2$$ 这是对应的$x$选文科,$y$选理科。 解得 $$a = b = \frac{v_1}{2}, c = d = \frac{v_2}{2}, e = \frac{v_1 + v_2}{2}$$ 那么我们只需要从$S$向每个点连一条容量为该同学选理科的愉悦值和他所有相邻的同学选理科的之和的有向边,从每个同学向$T$连一条容量为该同学选文科和他所有相邻的同学都选文科的愉悦值之和的有向边。 每个同学向它相邻的同学连一条容量为他们同时都选理科或者文科的愉悦值的平均值的双向边。 那么答案就是所有愉悦值的和减去最小割。 注意因为要算平均值,所以可能出现小数,这样在一开始连边的时候乘二,最后再除以二就行了。 ------------ # Code ```cpp #include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int N = 1000050; const int M = 2000050; const int INF = 999999999; int head[N], num = 1, n, m, s, t, tu[500][500][2], tmp[500][500], sum, d[N], x; struct Node { int next, to, flow; } edge[M * 2]; void Addedge(int u, int v, int w) { edge[++num] = (Node){head[u], v, w}; head[u] = num; return; } void Add(int u, int v, int w) { Addedge(u, v, w); Addedge(v, u, 0); return; } int Id(int a, int b) { return (a - 1) * m + b; } template <class T> void Read(T &x) { x = 0; int p = 0; char st = getchar(); while (st < '0' || st > '9') p = (st == '-'), st = getchar(); while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) +st - '0', st = getchar(); x = p ? -x : x; return; } template <class T> void Put(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) Put(x / 10); putchar(x % 10 + '0'); return; } bool Bfs() { queue<int> q; for (int i = 0; i <= t; i++) d[i] = 0; d[s] = 1; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = edge[i].next) if (!d[edge[i].to] && edge[i].flow) { d[edge[i].to] = d[u] + 1; q.push(edge[i].to); if (edge[i].to == t) return 1; } } return 0; } int Dinic(int x, int flow) { if (x == t || !flow) return flow; int rest = flow; for (int i = head[x]; i && rest; i = edge[i].next) if (edge[i].flow && d[edge[i].to] == d[x] + 1) { int v = edge[i].to; int tmp = Dinic(v, min(rest, edge[i].flow)); rest -= tmp; edge[i].flow -= tmp; edge[i ^ 1].flow += tmp; if (!tmp) d[v] = 0; } return flow - rest; } int Maxflow() { int maxflow = 0, tmp; while (Bfs()) { tmp = Dinic(s, INF); if (tmp) maxflow += tmp; } return maxflow; } int main() { Read(n); Read(m); t = n * m + 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { Read(x); sum += x; tu[i][j][1] += x * 2; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { Read(x); sum += x; tu[i][j][0] += x * 2; } for (int i = 1; i <= n - 1; i++) for (int j = 1; j <= m; j++) { Read(x); sum += x; tmp[i][j] += x; tu[i][j][1] += x; tu[i + 1][j][1] += x; } for (int i = 1; i <= n - 1; i++) for (int j = 1; j <= m; j++) { Read(x); sum += x; tmp[i][j] += x; Add(Id(i, j), Id(i + 1, j), tmp[i][j]); Add(Id(i + 1, j), Id(i, j), tmp[i][j]); tu[i][j][0] += x; tu[i + 1][j][0] += x; tmp[i][j] = 0; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m - 1; j++) { Read(x); sum += x; tmp[i][j] += x; tu[i][j][1] += x; tu[i][j + 1][1] += x; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m - 1; j++) { Read(x); sum += x; tmp[i][j] += x; Add(Id(i, j), Id(i, j + 1), tmp[i][j]); Add(Id(i, j + 1), Id(i, j), tmp[i][j]); tu[i][j][0] += x; tu[i][j + 1][0] += x; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { Add(s, Id(i, j), tu[i][j][0]); Add(Id(i, j), t, tu[i][j][1]); } Put(sum - Maxflow() / 2); return 0; } ```