题面
解法
建图方式有点意思……
- 简单来说,题意就是确定一些无向边的方向,使得三元环的个数最多。
- 考虑所有边都确定的情况下怎么计算三元环的个数,假设每一个点的度数为 d [ i ] d[i] d[i],通过容斥原理可以得到 ( n 3 ) − ∑ i = 1 n ( d [ i ] 2 ) {n\choose 3}-\sum_{i=1}^n{d[i]\choose 2} (3n)−∑i=1n(2d[i])。
- 那么我们可以将那些没有确定的边按照编号提取出来,编号为 i i i的边连接 ( S , i , 1 , 0 ) (S,i,1,0) (S,i,1,0),然后 i i i连接两个端点 x , y x,y x,y: ( i , x , 1 , 0 ) , ( i , y , 1 , 0 ) (i,x,1,0),(i,y,1,0) (i,x,1,0),(i,y,1,0)。
- 考虑如何维护后面 ∑ \sum ∑的部分,令 f ( i ) = ( d [ i ] 2 ) f(i)={d[i]\choose 2} f(i)=(2d[i]),不难发现 f ( i + 1 ) − f ( i ) > f ( i ) − f ( i − 1 ) f(i+1)-f(i)>f(i)-f(i-1) f(i+1)−f(i)>f(i)−f(i−1)。那么可以让点 i i i向 T T T连一些边,费用分别为 d [ i ] , d [ i ] + 1 , … d[i],d[i]+1,\dots d[i],d[i]+1,…。然后跑最小费用最大流。
- 考虑算法的正确性。在处理后半部分费用的边时,一定先走费用小的,即一定可以保证 d d d所表示的度数没有流过时比 d d d大的也一定没有流过。
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 110, M = 10110, inf = 1 << 30;
int cnt, c[M], d[M], dis[M], pre[M], las[M], head[M], used[M], a[N][N], x[N][N];
struct Node {int x, y;} b[N * N];
struct Edge {int next, num, c, w;} e[M * N];
void add(int x, int y, int c, int w) {
e[++cnt] = (Edge) {head[x], y, c, w};
head[x] = cnt;
}
void Add(int x, int y, int c, int w) {add(x, y, c, w), add(y, x, 0, -w);}
bool spfa(int s, int t) {
for (int i = s; i <= t; i++) dis[i] = inf, used[i] = 0;
queue <int> q; q.push(s), dis[s] = 0;
while (!q.empty()) {
int x = q.front(); q.pop(), used[x] = 0;
for (int p = head[x]; p; p = e[p].next) {
int k = e[p].num, c = e[p].c, w = e[p].w;
if (c && dis[k] > dis[x] + w) {
dis[k] = dis[x] + w, pre[k] = x, las[k] = p;
if (!used[k]) q.push(k), used[k] = 1;
}
}
}
return dis[t] != inf;
}
int EK(int s, int t) {
int ret = 0;
while (spfa(s, t)) {
int fl = inf;
for (int x = t; x != s; x = pre[x]) chkmin(fl, e[las[x]].c);
for (int x = t; x != s; x = pre[x])
e[las[x]].c -= fl, e[las[x] ^ 1].c += fl;
ret += dis[t] * fl;
}
return ret;
}
int main() {
int n, m = 0; read(n); cnt = 1;
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) read(a[i][j]);
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++) {
if (a[i][j] == 0) d[j]++, x[i][j] = 0, x[j][i] = 1;
if (a[i][j] == 1) d[i]++, x[i][j] = 1, x[j][i] = 0;
if (a[i][j] == 2) b[++m] = (Node) {i, j}, c[i]++, c[j]++;
}
int s = 0, t = n + m + 1, ans = n * (n - 1) * (n - 2) / 6;
for (int i = 1; i <= m; i++)
Add(s, i, 1, 0), Add(i, b[i].x + m, 1, 0), Add(i, b[i].y + m, 1, 0);
for (int i = 1; i <= n; i++) {
ans -= d[i] * (d[i] - 1) / 2;
for (int j = 0; j < c[i]; j++) Add(i + m, t, 1, d[i] + j);
}
cout << ans - EK(s, t) << "\n";
for (int i = 1; i <= m; i++)
for (int p = head[i]; p; p = e[p].next) {
int k = e[p].num, c = e[p].c;
if (k && c) {
int tx = k - m, ty = tx == b[i].x ? b[i].y : b[i].x;
x[tx][ty] = 0, x[ty][tx] = 1;
}
}
for (int i = 1; i <= n; i++, cout << "\n")
for (int j = 1; j <= n; j++) cout << x[i][j] << ' ';
return 0;
}