【BZOJ】3168: [Heoi2013]鈣鐵鋅硒維生素
阿新 • • 發佈:2018-12-10
題解
顯然我們既然初始矩陣就能通過線性變換變成單位矩陣,則該矩陣一定有逆
沒有逆輸出NIE
而且因為這些向量兩兩正交,則表示一個向量的時候表示方法唯一
那麼我們求一個逆可以求出這個矩陣消成單位矩陣的線性表示,再拿第二個矩陣和逆矩陣相乘可以得到第二個矩陣每個行向量用第一個矩陣的行向量唯一的表示方法
如果第二套的第k個行向量的表示裡第一行h個行向量係數不為0,則h可以被k替代
建圖二分圖匹配,先求一個匹配出來,然後對於每個點從前往後固定匹配看看能不能使得靠前的更小
說的很高階吧
算了我簡單一點說
就是一考慮初始的矩陣,什麼兩三行加加減減乘個係數,是可以消成單位1的,這個可以用類似高斯消元的方法解決,實際上如果你瞭解矩陣求逆的話你就知道我在給這個矩陣求逆矩陣
如果消不成就是NIE了
如果你不瞭解的話,你可以把每次變換每個行所用到的係數記下來,變成另一個矩陣\(B\)
那麼我們考慮第二套的某個向量,
\((b_{1},b_{2}...b_{n}) = \sum_{i = 1}^{n} c_{i} (a_{1},a_{2}..a_{n})\)
顯然如果\(c_{i}\)有數的話,我門可以把\(c_{i}\)移到等號左邊,把第二套的這個向量移到等式右邊,就證明\(i\)可以被這個向量換掉了
這個係數可以用兩個矩陣相乘求出來
圖建出來了,那就是跑二分圖了
我們先求出一個完備匹配來,沒有就是NIE,有的話對於\(1-n\)從小到大列舉能更新的匹配點,然後把這個點強制不選再跑二分圖看看會不會合法
程式碼
#include <bits/stdc++.h> #define fi first #define se second #define pii pair<int,int> #define pdi pair<db,int> #define mp make_pair #define pb push_back #define enter putchar('\n') #define space putchar(' ') #define eps 1e-8 #define mo 974711 #define MAXN 305 //#define ivorysi using namespace std; typedef long long int64; typedef double db; template<class T> void read(T &res) { res = 0;char c = getchar();T f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); } res *= f; } template<class T> void out(T x) { if(x < 0) {x = -x;putchar('-');} if(x >= 10) { out(x / 10); } putchar('0' + x % 10); } int N; int g[305][305],matc[305],matk[305]; bool vis[305]; bool flag = 0; struct Matrix { db f[305][305]; Matrix() {memset(f,0,sizeof(f));} void unit() { for(int i = 1 ; i <= N ; ++i) { f[i][i] = 1.0; } } friend Matrix operator * (const Matrix &a,const Matrix &b) { Matrix c; for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= N ; ++j) { for(int k = 1 ; k <= N ; ++k) { c.f[i][j] += a.f[i][k] * b.f[k][j]; } } } return c; } friend Matrix operator ~(Matrix a) { Matrix b; b.unit(); for(int i = 1 ; i <= N ; ++i) { int l = i; for(int j = i + 1; j <= N ; ++j) { if(fabs(a.f[j][i]) > fabs(a.f[l][i])) l = j; } if(fabs(a.f[l][i]) < 1e-8) {flag = 1;return b;} if(i != l) { for(int j = 1 ; j <= N ; ++j) { swap(a.f[i][j],a.f[l][j]); swap(b.f[i][j],b.f[l][j]); } } db t = 1.0 / a.f[i][i]; for(int j = 1 ; j <= N ; ++j) { a.f[i][j] *= t; b.f[i][j] *= t; } for(int j = 1 ; j <= N ; ++j) { if(i == j) continue; db t = a.f[j][i]; for(int k = 1 ; k <= N ; ++k) { a.f[j][k] -= t * a.f[i][k]; b.f[j][k] -= t * b.f[i][k]; } } } return b; } }A,B,C; bool match(int u) { for(int i = 1 ; i <= N ; ++i) { if(g[u][i]) { if(!vis[i]) { vis[i] = 1; if(!matc[i] || match(matc[i])) { matc[i] = u;matk[u] = i; return true; } } } } return false; } void Solve() { read(N); for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= N ; ++j) { scanf("%lf",&A.f[i][j]); } } for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= N ; ++j) { scanf("%lf",&C.f[i][j]); } } B = ~A; if(flag) { puts("NIE");return; } C = C * B; for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= N ; ++j) { if(fabs(C.f[i][j]) > 1e-8) { g[j][i] = 1; } } } for(int i = 1 ; i <= N ; ++i) { memset(vis,0,sizeof(vis)); if(!match(i)) { puts("NIE");return; } } puts("TAK"); for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= N ; ++j) { if(!g[i][j]) continue; if(matk[i] == j) break; if(matc[j] < i) continue; memset(vis,0,sizeof(vis)); for(int k = 1 ; k < i ; ++k) vis[matk[k]] = 1; vis[j] = 1; int t = matk[i]; matc[t] = 0; if(match(matc[j])) { matk[i] = j;matc[j] = i; break; } else { matc[t] = i; } } } for(int i = 1 ; i <= N ; ++i) { out(matk[i]);enter; } } int main() { #ifdef ivorysi freopen("f1.in","r",stdin); #endif Solve(); return 0; }