1. 程式人生 > 其它 >BZOJ 2396 - 神奇的矩陣

BZOJ 2396 - 神奇的矩陣

完蛋,下雨了。 目錄

題目

題目大意

給出三個行數和列數均為 \(n\) 的矩陣 \(A,B,C\),判斷 \(AB=C\) 是否成立。

資料範圍與提示

\(n\le 1000\),矩陣中的數字大於等於 \(0\) 小於 \(1000\),資料組數不超過 \(5\) 組。

解法

隨機一個 \(n\times 1\) 的列向量 \(x\),我們 \(\mathcal O(n^2)\) 地判斷 \(ABx\) 是否與 \(Cx\) 相等。關於 \(ABx\) 可以運用乘法結合律先算出 \(D=Bx\),再算出 \(ABx=AD\)

為啥捏?嘗試分析 \(P(AB\ne C\and ABx=Cx)\)

首先轉化一下 \(ABx=Cx\),發現滿足條件的 \(x\in \text{ker}(AB-C)\)

而我們有結論:\(\text{rank}(A)+\text{dim}(\text{ker}(A))=n\)。因為 \(AB\ne C\),所以 \(\text{rank}(AB-C)\ge 1\),從而推出 \(\text{dim}(\text{ker}(A))\le n-1\)

由此觀之,用 \(\text{ker}(AB-C)\) 組成子空間的維度至少比 \(V\) 的維度少 \(1\)。我們感性理解,從二維拓展到三維會使空間整點個數乘 \(t\)(其中 \(t\) 為值域包含整數個數),對於這道題大約就是 \(1000\)

。所以概率約為 \(\frac{1}{1000}\),為了保險可以多隨機幾次。

程式碼

#include <cstdio>

#define print(x,y) write(x),putchar(y) 

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

#include <ctime>
#include <cstdlib>
using namespace std;

const int maxn=1005;

int n,a[maxn][maxn],r[maxn];
int b[maxn][maxn],c[maxn][maxn];
int d[maxn];

int main() {
	srand(time(NULL)); 
	while(scanf("%d",&n)!=EOF) {
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				a[i][j]=read(9);
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				b[i][j]=read(9);
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				c[i][j]=read(9);
		bool flag=0;
		for(int T=1;T<=10;++T) {
			for(int i=1;i<=n;++i)
				r[i]=rand(),d[i]=0;
			for(int i=1;i<=n;++i)
				for(int j=1;j<=n;++j)
					d[i]+=b[i][j]*r[j];
			for(int i=1;i<=n;++i) {
				int t1=0,t2=0;
				for(int j=1;j<=n;++j)
					t1+=a[i][j]*d[j],
					t2+=c[i][j]*r[j]; 
				if(t1^t2) {
					flag=1; break;
				}
			}
			if(flag) break;
		}		
		puts(flag?"No":"Yes");
	}
	return 0;
}