题目大意:
给定一个完全图,每个队伍就是一个点,每场比赛就是一条无向边。现在每次可以消掉一个环,使得剩下的边最少。
思路:
有这样一个结论,将一个完全图分成若干个环逐个消去,所有节点出现在所有环上的总次数都相同。
举个例子,比如六阶完全图,可以拆成(0-index):
0 1 2 3 4 5
0 2 4
1 3 5
三个环,每个节点都总共出现了两次。
接下来我们可以 \(\mathcal O(n^3)\) 的进行模拟删环,记录答案。
枚举起点和步长,再 check 是否能构成环,如能构成则标记。
Code:
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) {
int n; cin >> n;
vector<vector<int>> ans(n, vector<int>(n)); //胜负关系,即答案
vector<vector<int>> ck(n, vector<int>(n)); //边被占用为1
for (int st = 0; st < n; st++) {
for (int len = 1; len < n; len++) {
vector<bool> vis(n); //点的访问情况
int now = st;
bool ok = true;
while (!vis[now]) {
vis[now] = true;
int nxt = (now + len) % n;
if (!ans[now][nxt] && !ans[nxt][now] && !ck[now][nxt] && !ck[nxt][now]) {
ck[now][nxt] = ck[nxt][now] = 1;
now = nxt;
} else {
ok = false;
break;
}
}
fill(vis.begin(), vis.end(), false);
now = st;
while (!vis[now]) { //线性删除边占用标记,不然会超时
vis[now] = true;
int nxt = (now + len) % n;
ck[now][nxt] = ck[nxt][now] = 0;
}
if (!ok) {
continue;
}
now = st;
fill(vis.begin(), vis.end(), false);
while (!vis[now]) {
// cerr << now << " ";
vis[now] = true;
int nxt = (now + len) % n;
ans[now][nxt] = 1;
ans[nxt][now] = -1;
now = nxt;
}
// cout << endl;
}
}
// cout << "####" << endl;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
cout << ans[i][j] << " \n"[j == n - 1 && i == n - 2];
}
}
}
return 0;
}