bzoj 3243: [Noi2013]向量内积

Description

两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即:
题面
现有 n 个d 维向量x1,...,xn ,小喵喵想知道是否存在两个向量的内积为k的倍数。请帮助她解决这个问题

Solution

首先做一个转换:如果把 \(B=A*A^T\) 构造出来,那么 \(B[i][j]\) 就代表向量 \(i\) 和向量 \(j\) 的内积,如果为 \(\mod k=0\) 则满足要求
\(A^T\) 是转置矩阵,也就是把原矩阵交换行列之后的矩阵

如果 \(k=2\)
我们只需要判断 \(A*A^T\) 是否和全 \(1\) 矩阵相等就行了
判断两个大矩阵相等一般用随机法:
随机一个行向量 \(E\),然后分别乘以两个矩阵判断行向量最后是否相等

行向量乘以矩阵的复杂度是 \(O(n*d)\) 的,所以复杂度就对了,在这个题利用矩乘的分配率 \(E*(A*A^T)=(E*A*)A^T\)
这样一次随机的正确性是 \(0.5\) 的(不会证),多随几次就可以了

\(k=3\)
由于矩阵中还有可能出现 \(2\) ,我们发现一个性质 \(2^2\mod 3=1\)
所以我们只需要把内积平方一下就可以了,即:
\((\sum_{i=1}^{d}a_i*b_i)*(\sum_{i=1}^{d}a_i*b_i)=\sum_{i=1}^{d}\sum_{j=1}^{d}a_i*b_i*a_j*b_j\)
相当于是构造出了一个大小为 \(n*d^2\) 的矩阵,还是像 \(k=2\) 那样做就可以了

#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=110;
int a[N][M],n,m,mod,A[N],B[N],x[N],id[M][M];
inline void check(int x){
	for(int i=1;i<=n;i++){
		if(x==i)continue;
		int s=0;
		for(int j=1;j<=m;j++)s+=a[i][j]*a[x][j];
		s%=mod;
		if(!s)printf("%d %d\n",min(i,x),max(i,x)),exit(0);
	}
}
inline void solve(){
	int sum=0;
	for(int i=1;i<=n;i++)sum=(sum+(x[i]=rand()%mod))%mod;
	for(int i=1;i<=m;i++)A[i]=0;
	for(int i=1;i<=n;i++)B[i]=0;	
	for(int j=1;j<=n;j++)
		for(int i=1;i<=m;i++)A[i]+=x[j]*a[j][i];
	for(int i=1;i<=m;i++)A[i]%=mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)B[i]+=A[j]*a[i][j];
	for(int i=1;i<=n;i++)B[i]%=mod;
	for(int i=1;i<=n;i++)if(B[i]!=sum)check(i);
}
inline void solvet(){
	int sum=0,t=0;
	for(int i=1;i<=n;i++)sum=(sum+(x[i]=rand()%mod))%mod;
	for(int i=1;i<=n;i++)B[i]=0;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)A[id[i][j]=++t]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=m;k++)A[id[j][k]]=(A[id[j][k]]+x[i]*a[i][j]*a[i][k])%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=m;k++)B[i]=(B[i]+A[id[j][k]]*a[i][j]*a[i][k])%mod;
	for(int i=1;i<=n;i++)if(B[i]!=sum)check(i);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  srand(time(NULL));
  scanf("%d%d%d",&n,&m,&mod);
  for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)scanf("%d",&a[i][j]),a[i][j]%=mod;
  int T=7;
  while(T--)mod==2?solve():solvet();
  puts("-1 -1");
  return 0;
}

posted @ 2018-04-28 22:19  PIPIBoss  阅读(268)  评论(0编辑  收藏  举报