BZOJ3168. [HEOI2013]鈣鐵鋅硒維生素(線性代數+二分圖匹配)
阿新 • • 發佈:2019-03-09
pri 們的 二分圖 exit 是否 splay find 行列式 res 可逆等價於矩陣 \(A\) 的行列式值 \(|A| \neq 0\),因此,我們的任務是快速求出矩陣 \(A\) 在替換了某一行的元素後的行列式的值。不難想到通過行列式按行展開法則來計算行列式的值,即假如替換的是矩陣 \(A\) 的第 \(i\) 行,那麽有(以下用 \(A_{i, j}\) 表示矩陣的 \((i, j)\) 元的代數余子式,矩陣的行列標號為 \(1 \sim n\)):
的值與第 \(i\) 行本身的元素無關,因此我們可以先用 \(O(n^3)\) 的時間求出矩陣 \(A\) 的伴隨矩陣,從而得到矩陣所有元素對應的代數余子式的值。具體地,設矩陣 \(A\) 的伴隨矩陣為 \(A^*\),根據 \(A^{-1} = \frac{1}{|A|}A^*\) 可得 \(A^* = |A|A^{-1}\),由於可以在 \(O(n^3)\) 的時間內求出矩陣 \(A\) 的逆矩陣 \(A^{-1}\)(並順便求出矩陣 \(A\) 的行列式值 \(|A|\)),因此自然能夠在相同的時間內求出伴隨矩陣。得到所有元素的代數余子式的值後,我們便能在 \(O(n)\) 的時間內求出單次行替換後矩陣的行列式值。一共需要進行 \(O(n^2)\) 次替換與判斷,故預處理出每個機器人對應的替換集合所需的總時間復雜度為 \(O(n^3)\)。
題目鏈接
https://www.lydsy.com/JudgeOnline/problem.php?id=3168
題解
首先,我們需要求出對於任意的 \(i, j(1 \leq i, j \leq n)\),第二套中的第 \(j\) 個機器人是否可以替換第一套中的第 \(i\) 個機器人。
將第 \(i\) 個機器人提供的第 \(j\) 種營養的量記為 \(a_{i, j}\),我們可以得到一個 \(n \times n\) 的矩陣 \(A\)。那麽,整套機器人能搭配出任何營養需求等價於將矩陣 \(A\) 化為行階梯形矩陣後擁有 \(n\) 個非零行,即該矩陣為滿秩矩陣。
由於滿秩矩陣即可逆矩陣,矩陣 \(A\)
\[|A| = \sum_\limits{k = 1}^{n} a_{i, k}A_{i, k}\]
其中的 \(a_{i, k}(1 \leq k \leq n)\) 為第 \(i\) 行替換後的元素值。由於代數余子式 \(A_{i, k}\)
預處理完畢後,我們就可以通過求二分圖匹配來尋找解的方案。不過,註意到題目要求求出字典序最小的匹配,因此直接通過一次二分圖匹配得到的方案並不是我們所需要的答案,我們需要在此基礎上再進行一次貪心。具體地,我們從小到大枚舉編號 \(i\),然後判斷在第一套機器人中編號小於 \(i\) 的機器人的匹配狀態不變的情況下,編號為 \(i\) 的機器人能否與編號更優的機器人匹配即可。
總時間復雜度為 \(O(n^3)\)。
代碼
#include<bits/stdc++.h>
using namespace std;
const int N = 3e2 + 10, mod = 1e9 + 7;
void add(int& x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
}
int mul(int x, int y) {
return (long long) x * y % mod;
}
int qpow(int v, int p) {
int result = 1;
for (; p; p >>= 1, v = mul(v, v)) {
if (p & 1) {
result = mul(result, v);
}
}
return result;
}
int n, a[N][N], b[N][N], inv[N][N], adj[N][N], choice[N], visit[N], tt, answer[N];
vector<int> go[N];
void transform1(int a[N][N], int i, int j) {
for (int p = 0; p < n; ++p) {
swap(a[i][p], a[j][p]);
}
}
void transform2(int a[N][N], int i, int k) {
for (int p = 0; p < n; ++p) {
a[i][p] = mul(a[i][p], k);
}
}
void transform3(int a[N][N], int i, int j, int k) {
for (int p = 0; p < n; ++p) {
add(a[i][p], mul(a[j][p], k));
}
}
void get_adj() {
int det = 1;
for (int i = 0; i < n; ++i) {
inv[i][i] = 1;
}
for (int i = 0; i < n; ++i) {
if (!a[i][i]) {
int p = i;
for (int j = i + 1; j < n; ++j) {
if (a[j][i]) {
p = j;
}
}
if (p == i) {
puts("NIE");
exit(0);
}
transform1(a, i, p);
transform1(inv, i, p);
det = (mod - det) % mod;
}
det = mul(det, a[i][i]);
int x = qpow(a[i][i], mod - 2);
transform2(a, i, x);
transform2(inv, i, x);
for (int j = i + 1; j < n; ++j) {
int p = a[j][i];
transform3(a, j, i, (mod - p) % mod);
transform3(inv, j, i, (mod - p) % mod);
}
}
for (int i = n - 1; ~i; --i) {
for (int j = i + 1; j < n; ++j) {
int p = a[i][j];
transform3(a, i, j, (mod - p) % mod);
transform3(inv, i, j, (mod - p) % mod);
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
adj[i][j] = mul(inv[j][i], det);
}
}
}
bool find(int u) {
for (int i = 0; i < go[u].size(); ++i) {
int v = go[u][i];
if (visit[v] != tt) {
visit[v] = tt;
if (!~choice[v] || find(choice[v])) {
choice[v] = u;
return true;
}
}
}
return false;
}
bool find_better(int u, int down) {
for (int i = 0; i < go[u].size(); ++i) {
int v = go[u][i];
if (visit[v] != tt) {
visit[v] = tt;
if (choice[v] == down || (choice[v] > down && find_better(choice[v], down))) {
answer[u] = v;
choice[v] = u;
return true;
}
}
}
return false;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
scanf("%d", &a[i][j]);
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
scanf("%d", &b[i][j]);
}
}
get_adj();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
int det = 0;
for (int k = 0; k < n; ++k) {
add(det, mul(b[j][k], adj[i][k]));
}
if (det) {
go[i].push_back(j);
}
}
}
memset(choice, -1, sizeof choice);
int total = 0;
for (int i = 0; i < n; ++i) {
++tt;
total += find(i);
}
if (total != n) {
puts("NIE");
} else {
puts("TAK");
for (int i = 0; i < n; ++i) {
++tt;
find_better(i, i);
printf("%d\n", answer[i] + 1);
}
}
return 0;
}
BZOJ3168. [HEOI2013]鈣鐵鋅硒維生素(線性代數+二分圖匹配)