线性代数一之矩阵转向量随机化求解——神奇的矩阵(BZOJ)+向量内积


矩阵既可以看成是一张数位表,也可以看成是若干个行向量或者若干个列向量的向量表

在这里插入图片描述

神奇的矩阵

description

solution

暴力做 A ∗ B A*B AB会达到 n 3 n^3 n3的复杂度,难以接受

考虑,如果对于矩阵 A , B , C A,B,C A,B,C满足 A ∗ B = C A*B=C AB=C,显然有 A ∗ B ∗ R = C ∗ R A*B*R=C*R ABR=CR

于是有随机一个 1 × n 1\times n 1×n的向量 R R R,然后check等式是否成立, A ∗ R ∗ B A*R*B ARB就会降成 n 2 n^2 n2的复杂度

随机多次都无法满足这个式子, A ∗ B = C A*B=C AB=C的概率就微乎其微(除非你是非酋)

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;

struct matrix {
	int n, m;
	int c[1000][1000];
	matrix() {
		memset( c, 0, sizeof( c ) );
	}
	matrix operator * ( matrix &t ) {
		matrix ans;
		ans.n = n, ans.m = t.m;
		for( int i = 0;i <= n;i ++ )
			for( int j = 0;j <= t.m;j ++ )
				for( int k = 0;k <= m;k ++ )
					ans.c[i][j] += c[i][k] * t.c[k][j];
		return ans;
	}
}A, B, C, R, ans1, ans2;

signed main() {
	srand( time( 0 ) );
	next :
	while( ~ scanf( "%lld", &n ) ) {
		n --;
		A.n = A.m = B.n = B.m = C.n = C.m = n;
		for( int i = 0;i <= n;i ++ )
			for( int j = 0;j <= n;j ++ )
				scanf( "%lld", &A.c[i][j] );
		for( int i = 0;i <= n;i ++ )
			for( int j = 0;j <= n;j ++ )
				scanf( "%lld", &B.c[i][j] );
		for( int i = 0;i <= n;i ++ )
			for( int j = 0;j <= n;j ++ )
				scanf( "%lld", &C.c[i][j] );
		int t = 30;
		again :
		while( t -- ) {
			R.n = 0, R.m = n;
			for( int i = 0;i <= n;i ++ )
				R.c[0][i] = rand();
			ans1 = R * A * B;
			ans2 = R * C;
			for( int i = 0;i <= n;i ++ )
				if( ans1.c[0][i] != ans2.c[0][i] )
					goto again;
			printf( "Yes\n" );
			goto next;
		}
		printf( "No\n" );
	}
	return 0;
} 

[NOI2013]向量内积

description

solution

  • k=2

    • 求出矩阵两两内积 ( m o d 2 ) \pmod 2 (mod2) ,即 Y = A ∗ A T Y=A*A^T Y=AAT
    • 接下来就是判断 Y = E , E Y=E,E Y=E,E为全 1 1 1矩阵( Y i , j Y_{i,j} Yi,j代表着 A A A i i i行向量与 A T A^T AT j j j列向量也就是原来的 A j A_j Aj行向量的内积)因为如果全 1 1 1代表着每两个向量的内积都为 1 ( m o d 2 ) 1\pmod 2 1(mod2)
      • 判断方法就是上一题的随机化
      • 只要不等,就会有一个 0 0 0向量,找到其位置 p o s pos pos,最后暴力求每个向量与其的内积是否整除 k k k即可
  • k=3,此时 A i , j = 0 / 1 / 2 A_{i,j}=0/1/2 Ai,j=0/1/2,不能在使用上述 E E E来判断了

    • 转换一下即可, Z i , j = Y i , j 2 ( m o d 3 ) Z_{i,j}=Y_{i,j}^2\pmod 3 Zi,j=Yi,j2(mod3),有 1 2 ≡ 2 3 ≡ 1 ( m o d 3 ) 1^2\equiv 2^3\equiv 1\pmod 3 12231(mod3),只有 0 2 ≡ 0 ( m o d 3 ) 0^2\equiv 0\pmod3 020(mod3)

    • 再次使用 E E E来进行判断

    • 问题在于, Z Z Z Y Y Y每个单项的平方,不是整体的平方,不能使用矩阵快速得到

      • α \alpha α是随机的一个 1 × n 1\times n 1×n向量

      • ( Z ∗ α ) i = ∑ j = 1 n Z i , j ∗ α j = ∑ j = 1 n Y i , j 2 ∗ α j = ∑ j = 1 n α j ( ∑ k = 1 n A i , k A k , j T ) 2 (Z*\alpha)_i=\sum_{j=1}^nZ_{i,j}*\alpha_j=\sum_{j=1}^nY_{i,j}^2*\alpha_j=\sum_{j=1}^n\alpha_j\bigg(\sum_{k=1}^nA_{i,k}A^T_{k,j}\bigg)^2 (Zα)i=j=1nZi,jαj=j=1nYi,j2αj=j=1nαj(k=1nAi,kAk,jT)2

        = ∑ j = 1 n α j ∑ k 1 = 1 n A i , k 1 A k 1 , j T ∗ ∑ j = 1 n α j ∑ k 2 = 1 n A i , k 2 A k 2 , j T =\sum_{j=1}^n\alpha_j\sum_{k_1=1}^nA_{i,k_1}A^T_{k_1,j}*\sum_{j=1}^n\alpha_j\sum_{k_2=1}^nA_{i,k_2}A^T_{k_2,j} =j=1nαjk1=1nAi,k1Ak1,jTj=1nαjk2=1nAi,k2Ak2,jT

      • 发现可以变为 ∑ k 1 , k 2 A i , k 1 A i , k 2 ∗ ∑ j = 1 n α j A k 1 , j A k 2 , j T \sum_{k_1,k_2}A_{i,k_1}A_{i,k_2}*\sum_{j=1}^n\alpha_jA_{k_1,j}A^T_{k_2,j} k1,k2Ai,k1Ai,k2j=1nαjAk1,jAk2,jT

        预处理出和 i i i无关部分,设 g k 1 , k 2 = ∑ j = 1 n α j A k 1 , j A k 2 , j T g_{k_1,k_2}=\sum_{j=1}^n\alpha_jA_{k_1,j}A^T_{k_2,j} gk1,k2=j=1nαjAk1,jAk2,jT

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100005
#define maxd 105
int n, d, k, sum, pos, flag;
int x[maxn][maxd], g[maxn][maxd];
int ret[maxd], r[maxn];

void calc2() {
	for( int i = 1;i <= d;i ++ ) 
		ret[i] = 0;
	for( int i = 1;i <= d;i ++ )
		for( int j = 1;j <= n;j ++ )
			ret[i] = ( ret[i] + r[j] * x[j][i] ) % k;
	for( int i = 1;i <= n;i ++ ) {
		int ans = 0;
		for( int j = 1;j <= d;j ++ )
			ans = ( ans + ret[j] * x[i][j] ) % k;
		if( ans != sum ) {
			pos = i, flag = 1;
			break;
		}
	}
}

void calc3() {
	for( int k1 = 1;k1 <= d;k1 ++ )
		for( int k2 = 1;k2 <= d;k2 ++ ) {
			g[k1][k2] = 0;
			for( int j = 1;j <= n;j ++ )
				g[k1][k2] = ( g[k1][k2] + r[j] * x[j][k1] % k * x[j][k2] ) % k;
		}
	for( int i = 1;i <= n;i ++ ) {
		int ans = 0;
		for( int k1 = 1;k1 <= d;k1 ++ )
			for( int k2 = 1;k2 <= d;k2 ++ )
				ans = ( ans + x[i][k1] * x[i][k2] % k * g[k1][k2] ) % k;
		if( ans != sum ) {
			pos = i, flag = 1;
			break;
		}
	}
}

signed main() {
	srand( time( 0 ) );
	scanf( "%lld %lld %lld", &n, &d, &k );
	for( int i = 1;i <= n;i ++ )
		for( int j = 1;j <= d;j ++ )
			scanf( "%lld", &x[i][j] );	
	for( int T = 1;T <= 6;T ++ ) {
		sum = 0;
		for( int i = 1;i <= n;i ++ )
			r[i] = rand() % k, sum = ( sum + r[i] ) % k;
		if( k == 2 ) calc2();
		else calc3();
		if( flag ) break;
	}
	if( ! flag ) return ! printf( "-1 -1\n" );
	else {
		for( int i = 1;i <= n;i ++ )
			if( i ^ pos ) {
				int ans = 0;
				for( int j = 1;j <= d;j ++ )
					ans = ( ans + x[i][j] * x[pos][j] ) % k;
				if( ! ans ) return ! printf( "%lld %lld\n", min( i, pos ), max( i, pos ) );
			}
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值