考虑竞赛图三元环计数,设第\(i\)个点的入度为\(d_i\),根据容斥,答案为\(C_n^3 - \sum C_{d_i}^2\)
所以我们需要最小化\(\sum C_{d_i}^2\)
考虑将\(C_{d_i}^2\)差分,然后通过费用流解决
下面\((a,b)\)边表示流量为\(a\)、费用为\(b\)的边
建图:
每一场比赛和每一个人都建一个点
\(S\)向每一场比赛连\((1,0)\)边
每一场比赛若不确定结果则向两个参与者连\((1,0)\)边,如果胜者确定则只向胜者连\((1,0)\)边
每一个选手向\(T\)连\((1,C_i^2-C_{i-1}^2)(i \in [1,N])\)边
因为\(C_i^2-C_{i-1}^2=i-1\),所以\(j>i \rightarrow C_j^2 - C_{j-1}^2 > C_i^2 - C_{i-1}^2\),所以每一次一场比赛确定胜者能够正确地增加TA的贡献。
跑最小费用最大流,最后的答案就是最小的\(\sum C_{d_i}^2\)
输出方案考虑每一场比赛对应的点的流流向哪一个选手即可
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c) && c != EOF){
if(c == '-')
f = 1;
c = getchar();
}
if(c == EOF)
exit(0);
while(isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 20010 , MAXM = 500010;
struct Edge{
int end , upEd , f , c;
}Ed[MAXM];
int head[MAXN] , dis[MAXN] , pre[MAXN] , flo[MAXN] , ans[110][110];
int N , S , T , cntEd = 1;
bool vis[MAXN];
queue < int > q;
inline void addEd(int a , int b , int c , int d = 0){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
Ed[cntEd].f = c;
Ed[cntEd].c = d;
head[a] = cntEd;
}
inline bool SPFA(){
memset(dis , 0x3f , sizeof(dis));
dis[S] = 0;
while(!q.empty())
q.pop();
q.push(S);
flo[S] = INF;
while(!q.empty()){
int t = q.front();
q.pop();
vis[t] = 0;
for(int i = head[t] ; i ; i = Ed[i].upEd)
if(Ed[i].f && dis[Ed[i].end] > dis[t] + Ed[i].c){
dis[Ed[i].end] = dis[t] + Ed[i].c;
flo[Ed[i].end] = min(Ed[i].f , flo[t]);
pre[Ed[i].end] = i;
if(!vis[Ed[i].end]){
vis[Ed[i].end] = 1;
q.push(Ed[i].end);
}
}
}
return dis[T] != dis[T + 1];
}
int EK(){
int ans = 0;
while(SPFA()){
int cur = T , sum = 0;
while(cur != S){
sum += Ed[pre[cur]].c;
Ed[pre[cur]].f -= flo[T];
Ed[pre[cur] ^ 1].f += flo[T];
cur = Ed[pre[cur] ^ 1].end;
}
ans += sum * flo[T];
}
return ans;
}
void input(){
N = read();
int cnt = N;
for(int i = 1 ; i <= N ; ++i){
for(int j = 1 ; j <= i ; ++j)
read();
for(int j = i + 1 ; j <= N ; ++j){
int a = read();
addEd(S , ++cnt , 1);
addEd(cnt , S , 0);
if(a != 1){
addEd(cnt , j , 1);
addEd(j , cnt , 0);
}
if(a){
addEd(cnt , i , 1);
addEd(i , cnt , 0);
}
}
}
T = ++cnt;
for(int i = 1 ; i <= N ; ++i)
for(int j = 1 ; j <= N ; ++j){
addEd(i , T , 1 , j * (j - 1) / 2 - (j - 1) * (j - 2) / 2);
addEd(T , i , 0 , -(j * (j - 1) / 2 - (j - 1) * (j - 2) / 2));
}
}
void work(){
cout << N * (N - 1) * (N - 2) / 6 - EK() << endl;
int cnt = N;
for(int i = 1 ; i <= N ; ++i)
for(int j = 1 ; j <= N ; ++j)
if(i != j)
if(i > j)
ans[i][j] = ans[j][i] ^ 1;
else{
++cnt;
for(int k = head[cnt] ; k ; k = Ed[k].upEd)
if(!Ed[k].f){
ans[i][j] = Ed[k].end == i;
break;
}
}
for(int i = 1 ; i <= N ; ++i){
for(int j = 1 ; j <= N ; ++j)
cout << ans[i][j] << ' ';
cout << endl;
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in" , "r" , stdin);
//freopen("out" , "w" , stdout);
#endif
input();
work();
return 0;
}