【矩阵乘法】[NOI2013]向量内积

来源:互联网 发布:java自定义泛型类 编辑:程序博客网 时间:2024/04/28 12:30

题目描述

两个 d 维向量 A=[a1,a2,,ad]B=[b1,b2,,bd] 的内积为其相对应维度的权值的乘积和,即:

A,B=i=1daibi=a1b1+a2b2++adbd

现在有 nd 维向量 x1,x2,,xn,小喵喵想知道是否存在两个向量的内积为 k 的倍数。请帮助她解决这个问题。

输入格式

第一行包含 3 个正整数 n,d,k,分别表示向量的个数,维数以及待检测的倍数。

接下来 n 行每行有 d 个非负整数,其中第 i 行的第 j 个整数表示向量 xi 的第 j 维权值 xi,j

输出格式

包含两个整数,用空格隔开。

如果存在两个向量 xp,xq 的内积为 k 的整数倍,则输出两个向量的编号 pq (要求 p<q)。如果存在多组这样的向量组合,输出其中任意一组即可。

若不存在这样的向量组合,则输出两个 1

样例一

input

3 5 21 0 1 0 11 1 0 1 00 1 0 1 1

output

2 3

explanation

x1,x2=1x1,x3=1x2,x3=2

限制与约定

测试点编号ndkxi,j12202102520210310203104202021005502031006505021000750503300000088080230000009100100330000001050010033000000111000100230000001210001003300000013100001002<1014100001003<1015150001002<1016180001002<1017200001002<101850000303<101980000303<1020100000303<10

时间限制:5s

空间限制:256MB

分析

注意:以下运算都在模k意义下

k=2

我们把这些向量看成一个n×d的矩阵A

A=x1,1x2,1xn,1x1,2x2,2xn,2x1,3x2,3xn,3x1,dx2,dxn,d

A的转置矩阵AT=x1,1x1,2x1,nx2,1x2,2x2,nx3,1x3,2x3,nxn,1xn,2xnn

B=A×AT,那么bi,j就表示xixj,如果B矩阵除了主对角线全部是1的话,显然无解。
但是求出B矩阵的时间显然是O(n2d),显然是无法通过本题的。
我们令Bbi,i=xixi,其余元素为1的矩阵。
如果B=B的话,就无解。
我们再随机找出一个列向量C (其实就是全是1,因为0没用。。。)

B×C=B×CA×AT×C=B×CA×(AT×C)=B×C

D=AT×C我们可以用O(nd)的时间求出来,A×D也可以用O(nd)的时间求出来,B×C可以O(n)求出。
然后比较A×(AT×C)  B×C这两个列向量的每一个元素是否相同,如果第i行的元素不相同,说明xi肯定是答案只要,枚举找出xj即可。
尽管有出错的概率,但是这个算法还是比较可靠的。

k=3

我们发现

1×12×21(mod3)

所以,我们令B=(AAT)2其余矩阵定义不变,那么展开后会发现
A=x1,1×x1,1x2,1×x2,1xn,1×xn,1x1,1×x1,2x2,1×x2,2xn,1×xn,2x1,1×x1,3x2,1×x2,3xn,1×xn,3x1,1×x1,dx2,1×x2,dxn,1×xn,dx1,2×x1,1x2,2×x2,1xn,2×xn,1x1,2×x1,2x2,2×x2,2xn,2×xn,2x1,d×x1,dx2,d×x2,dxn,d×xn,dB=A×AT

然后参考k=2的算法即可,由于k=3时矩阵太大,内存开不下,但是算的时候按照规则算就行了。

代码

#include<cstdio>#include<algorithm>using namespace std;#define MAXN 100000#define MAXM 100int n,d,k,a[MAXN+10][MAXM+10],s[MAXN+10],c[MAXM*MAXM+10],g[MAXN+10];void Read(int &x){    char c;    while(c=getchar(),c!=EOF)        if(c>='0'&&c<='9'){            x=c-'0';            while(c=getchar(),c>='0'&&c<='9')                x=x*10+c-'0';            ungetc(c,stdin);            return;        }}int Get_id(int i,int j){    return (i-1)*d+j;}void read(){    Read(n),Read(d),Read(k);    int i,j;    for(i=1;i<=n;i++)        for(j=1;j<=d;j++){            Read(a[i][j]);            a[i][j]%=k;            s[i]+=a[i][j]*a[i][j];        }    for(i=1;i<=n;i++)        s[i]%=k;}void solve2(){    int i,j,k,sum;    for(i=1;i<=d;i++)        for(j=1;j<=n;j++)            c[i]^=a[j][i];    for(i=1;i<=n;i++)        for(j=1;j<=d;j++)            g[i]^=a[i][j]&c[j];    for(i=1;i<=n;i++)        if(g[i]!=(s[i]^((n-1)&1)))            break;    if(i>n){        puts("-1 -1");        return;    }    for(j=1;j<=n;j++)        if(i!=j){            sum=0;            for(k=1;k<=d;k++)                sum^=a[i][k]&a[j][k];            if(!sum)                break;        }    if(i>j)        swap(i,j);    printf("%d %d\n",i,j);}void solve3(){    int i,j,k,sum;    for(i=1;i<=d;i++)        for(j=1;j<=d;j++)            for(k=1;k<=n;k++)                c[Get_id(i,j)]+=a[k][i]*a[k][j];    for(i=d*d;i;i--)        c[i]%=3;    for(i=1;i<=n;i++)        for(j=1;j<=d;j++)            for(k=1;k<=d;k++)                g[i]+=a[i][j]*a[i][k]*c[Get_id(j,k)];    for(i=1;i<=n;i++)        if(g[i]%3!=((n-1)+s[i]*s[i])%3)            break;    if(i>n){        puts("-1 -1");        return;    }    for(j=1;j<=n;j++)        if(j!=i){            sum=0;            for(k=1;k<=d;k++)                sum=(sum+a[i][k]*a[j][k])%3;            if(!sum)                break;        }    if(i>j)        swap(i,j);    printf("%d %d\n",i,j);}int main(){    read();    if(k==2)        solve2();    else        solve3();}
0 0
原创粉丝点击