天天看点

【矩阵乘法】[NOI2013]向量内积题目描述分析代码

题目描述

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

⟨A,B⟩=∑i=1daibi=a1b1+a2b2+⋯+adbd

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

输入格式

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

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

输出格式

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

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

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

样例一

input

3 5 2
1 0 1 0 1
1 1 0 1 0
0 1 0 1 1

      

output

2 3

      

explanation

⟨x1,x2⟩=1 , ⟨x1,x3⟩=1 , ⟨x2,x3⟩=2 。

限制与约定

测试点编号 n d k xi,j
1 2 20 2 ≤10
2 5 20 2 ≤10
3 10 20 3 ≤10
4 20 20 2 ≤100
5 50 20 3 ≤100
6 50 50 2 ≤1000
7 50 50 3 ≤3000000
8 80 80 2 ≤3000000
9 100 100 3 ≤3000000
10 500 100 3 ≤3000000
11 1000 100 2 ≤3000000
12 1000 100 3 ≤3000000
13 10000 100 2 <10
14 10000 100 3 <10
15 15000 100 2 <10
16 18000 100 2 <10
17 20000 100 2 <10
18 50000 30 3 <10
19 80000 30 3 <10
20 100000 30 3 <10

时间限制: 5s

空间限制: 256MB

分析

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

k=2

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

A=⎡⎣⎢⎢⎢⎢⎢x1,1x2,1⋮xn,1x1,2x2,2⋮xn,2x1,3x2,3⋮xn,3⋯⋯⋱⋯x1,dx2,d⋮xn,d⎤⎦⎥⎥⎥⎥⎥

A 的转置矩阵AT=⎡⎣⎢⎢⎢⎢⎢x1,1x1,2⋮x1,nx2,1x2,2⋮x2,nx3,1x3,2⋮x3,n⋯⋯⋱⋯xn,1xn,2⋮xnn⎤⎦⎥⎥⎥⎥⎥

令 B=A×AT ,那么 bi,j 就表示 xi⋅xj ,如果 B 矩阵除了主对角线全部是1的话,显然无解。

但是求出B矩阵的时间显然是 O(n2d) ,显然是无法通过本题的。

我们令 B′ 为 bi,i=xi⋅xi ,其余元素为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×1≡2×2≡1(mod3)

所以,我们令 B=(A∗AT)2 其余矩阵定义不变,那么展开后会发现

A′=⎡⎣⎢⎢⎢⎢⎢x1,1×x1,1x2,1×x2,1⋮xn,1×xn,1x1,1×x1,2x2,1×x2,2⋮xn,1×xn,2x1,1×x1,3x2,1×x2,3⋮xn,1×xn,3⋯⋯⋱⋯x1,1×x1,dx2,1×x2,d⋮xn,1×xn,dx1,2×x1,1x2,2×x2,1⋮xn,2×xn,1x1,2×x1,2x2,2×x2,2⋮xn,2×xn,2⋯⋯⋱⋯x1,d×x1,dx2,d×x2,d⋮xn,d×xn,d⎤⎦⎥⎥⎥⎥⎥B=A′×A′T

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

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100000
#define MAXM 100
int n,d,k,a[MAXN+][MAXM+],s[MAXN+],c[MAXM*MAXM+],g[MAXN+];
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*+c-'0';
            ungetc(c,stdin);
            return;
        }
}
int Get_id(int i,int j){
    return (i-)*d+j;
}
void read(){
    Read(n),Read(d),Read(k);
    int i,j;
    for(i=;i<=n;i++)
        for(j=;j<=d;j++){
            Read(a[i][j]);
            a[i][j]%=k;
            s[i]+=a[i][j]*a[i][j];
        }
    for(i=;i<=n;i++)
        s[i]%=k;
}
void solve2(){
    int i,j,k,sum;
    for(i=;i<=d;i++)
        for(j=;j<=n;j++)
            c[i]^=a[j][i];
    for(i=;i<=n;i++)
        for(j=;j<=d;j++)
            g[i]^=a[i][j]&c[j];
    for(i=;i<=n;i++)
        if(g[i]!=(s[i]^((n-)&)))
            break;
    if(i>n){
        puts("-1 -1");
        return;
    }
    for(j=;j<=n;j++)
        if(i!=j){
            sum=;
            for(k=;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=;i<=d;i++)
        for(j=;j<=d;j++)
            for(k=;k<=n;k++)
                c[Get_id(i,j)]+=a[k][i]*a[k][j];
    for(i=d*d;i;i--)
        c[i]%=;
    for(i=;i<=n;i++)
        for(j=;j<=d;j++)
            for(k=;k<=d;k++)
                g[i]+=a[i][j]*a[i][k]*c[Get_id(j,k)];
    for(i=;i<=n;i++)
        if(g[i]%!=((n-)+s[i]*s[i])%)
            break;
    if(i>n){
        puts("-1 -1");
        return;
    }
    for(j=;j<=n;j++)
        if(j!=i){
            sum=;
            for(k=;k<=d;k++)
                sum=(sum+a[i][k]*a[j][k])%;
            if(!sum)
                break;
        }
    if(i>j)
        swap(i,j);
    printf("%d %d\n",i,j);
}
int main()
{
    read();
    if(k==)
        solve2();
    else
        solve3();
}
           

继续阅读