bzoj 3243: [Noi2013]向量内积 矩阵乘法

        这道题目中D应该是<=100。。

       我们可以把两个向量的点积看成1*d和d*1的矩阵相乘;那么我们将原始矩阵A转置得到A',A*A'[i,j]就是向量i*向量j的点积。

       但是这样是O(N^2D)的;考虑用一些黑科技。考虑mod=2时,假设对于i,我们求出i之前的所有向量与i的点积的和;如果所有的点积都>0即=1,那么显然点积的和对二取模=(i-1)%2;否则如果≠(i-1)%2,显然i与i前面的某一个向量的点积=0,我们O(ND)寻找答案即可。但是这样不一定能得到解,我们不妨随机打乱向量的顺序然后判断,这样至少是1-(1/2)^5的正确率了。。

       当mod=3时也是一样的,不过点积>0并不一定=1,但是注意到点积的平方>0则一定=1,把点积拆开来计算即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
#define M 101
#define rd ((rand_now=((long long)rand_now*20000909+2010527)%1000000009)/97)
using namespace std;

int n,m,mod,rand_now=1000003,a[N][M],q[N],b[M],c[M][M];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
bool check(int x,int y){
	int i,tmp=0;
	for (i=1; i<=m; i++) tmp+=a[x][i]*a[y][i];
	return !(tmp%mod);
}
int solve(int x){
	int ans=0,i,j;
	if (mod==2)
		for (i=1; i<=m; b[i]^=a[x][i],i++)
			ans^=b[i]&a[x][i];
	else
		for (i=1; i<=m; i++)
			for (j=1; j<=m; c[i][j]+=a[x][i]*a[x][j],j++)
				ans+=c[i][j]*a[x][i]*a[x][j];
	return ans%mod;
}
int main(){
	n=read(); m=read(); mod=read(); int i,j;
	for (i=1; i<=n; i++)
		for (j=1; j<=m; j++) a[i][j]=read()%mod;
	for (i=1; i<=n; i++) q[i]=i;
	int cas=7-mod;
	while (cas--){
		if (mod==2) memset(b,0,sizeof(b)); else memset(c,0,sizeof(c));
		for (i=2; i<=n; i++) swap(q[i],q[rd%(i-1)+1]);
		for (i=1; i<=n; i++) if (solve(q[i])!=(i-1)%mod)
			for (j=1; j<i; j++) if (check(q[i],q[j])){
				if (q[i]>q[j]) swap(i,j);
				printf("%d %d\n",q[i],q[j]); return 0;
			}
	}
	puts("-1 -1");
	return 0;
}


by lych

2016.5.23

你可能感兴趣的:(随机算法,矩阵乘法)