3243: [Noi2013]向量内积

3243: [Noi2013]向量内积

Time Limit: 10 Sec   Memory Limit: 256 MBSec   Special Judge
Submit: 1269   Solved: 252
[ Submit][ Status][ Discuss]

Description

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

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

Input

第一行包含3个正整数n,d,k,分别表示向量的个数,维数以及待检测的倍数。接下来n行每行有d个非负整数,其中
第i行的第j个整数表示向量xi的第j维权值xi,j。
N<=100000,D<=30,K<=3,Xi,j<10

Output

包含两个整数,用空格隔开。如果存在两个向量xp,xq的内积为k的整数倍,则输出两个向量的编号p与q(要求p
)。如果存在多组这样的向量组合,输出其中任意一组即可。若不存在这样的向量组合,则输出两个-1。

Sample Input

2 20 2
0 0 1 1 1 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1
1 0 1 0 1 0 1 1 1 1 0 1 1 1 0 1 1 0 1 0

Sample Output

1 2

HINT

新增数据一组,但未重测By TA1111,2016.5.17

Source

[ Submit][ Status][ Discuss]

将n个向量写在一个n*d的矩阵A中与它的转置矩阵相乘,得到A*AT = B
这样矩阵B中任一元素Bi,j就是向量i与向量j的内积了,可以直接检查是否符合题意
但是这样做复杂度仍然是O(n^2*d),,无法通过
对于矩阵乘法操作的时候,顺便%k
那么,对于B中任一元素Bi,j,若其等于0,则说明i,j的内积符合题意
先考虑k == 2的时候怎么做
假设B是个全1矩阵,如果左右相等,那么不存在符合题意的i,j
否则如果Bi,j不为0,那么对于向量i,我们可以暴力O(n*d)找出j
因为判断左右是否相等是可以O(n*d)的,出错概率不超过1/2
做法是两边同时乘以一个1*n的随机矩阵C,直接判结果是否相等
不过这样做要注意,Bi,i的值,这样的东西是不符合的,因此我们要强制让i,i的内积为1
这样只需左边做完特殊处理即可

如果k == 3,B不能用全1矩阵了,因为结果可能有2
但是2 ≡ -1(mod 3)
因此,如果我们将所有结果平方,那么B就能用全1矩阵了
不过此时是n*(d^2)的矩阵与(d^2)*n的矩阵相乘
因为


bzoj上该题题面有误,,比如存在n = 10000,d = 100的数据啦= =
然后就是时限略紧,原题每个点5s,但是bzoj18个点一共10s。。。。
苟蒻用剪枝 + 卡常3s过了
剪枝的话,左边的矩阵乘法一共要两次,进行到第二次的时候边做边判断,找到不合法马上跳到输出步骤
卡常的话,首先尽量减少%运算,因为这玩意略慢,然后用指针将二维数组变为一维,寻址快
#include  
#include  
#include  
#include  
using namespace std;  
    
const int N = 1E5 + 10;  
    
int n,d,k,pos,g[N],Z[N],W[N],a[N][111],T[N],A[N],B[N];  
    
int Mul_Vector (int x,int y)  
{  
    int ret = 0,*nx = a[x],*ny = a[y];  
    for (int i = 1; i <= d; i++)  
        ret += nx[i]*ny[i];
    return ret%k;  
} 
    
void Print()  
{  
    for (int i = 1; i <= n; i++)  
        if (i != pos && Mul_Vector(pos,i) == 0)  
            {printf("%d %d\n",min(i,pos),max(i,pos)); return;}  
}  
    
void Solve2()  
{    
    for (int I = 1; I <= 10; I++)  
    {
        int tot = 0;  
        for (int i = 1; i <= n; i++)  
            T[i] = rand()%k,tot += T[i];  
        tot %= k;  
        for (int i = 1; i <= d; i++) W[i] = 0;  
        for (int i = 1; i <= n; i++) Z[i] = 0;
        for (int j = 1; j <= n; j++)
        {
            int *na = a[j];
            for (int i = 1; i <= d; i++) W[i] += T[j]*na[i];
        }  
        for (int i = 1; i <= d; i++) W[i] %= k;
        for (int i = 1; i <= n; i++)  
        {  
            int *na = a[i];
            for (int j = 1; j <= d; j++) Z[i] += W[j]*na[j];  
            Z[i] += (1-g[i])*T[i]; Z[i] %= k; Z[i] += k;  
            if (Z[i] % k != tot) {pos = i; Print(); return;}  
        }  
    }
    puts("-1 -1");  
}  
    
void Solve3()  
{  
    for (int i = 1; i <= n; i++) g[i] = g[i]*g[i]%k;
    int D = d*d;
    for (int i = 1; i <= D; i++)
    {
        int t = (i % d == 0)?i/d:i/d+1;
        A[i] = t; B[i] = i-(t-1)*d;
    }
    for (int I = 1; I <= 10; I++) 
    {
        int tot = 0;  
        for (int i = 1; i <= n; i++)  
            T[i] = rand()%k,tot += T[i];  
        tot %= k;  
        for (int i = 1; i <= D; i++) W[i] = 0;  
        for (int i = 1; i <= n; i++) Z[i] = 0;  
        for (int j = 1; j <= n; j++)
        {
            int *na = a[j];
            for (int i = 1; i <= D; i++) W[i] += T[j]*na[A[i]]*na[B[i]];
        }
        for (int i = 1; i <= D; i++) W[i] %= k;
        for (int i = 1; i <= n; i++)  
        {  
            int *na = a[i];
            for (int j = 1; j <= D; j++) Z[i] += W[j]*na[A[j]]*na[B[j]]; 
            Z[i] += (1-g[i])*T[i]; Z[i] %= k; Z[i] += k;  
            if (Z[i] % k != tot) {pos = i; Print(); return;}  
        }  
    }
    puts("-1 -1");  
}  
    
int getint()  
{  
    char ch = getchar();  
    int ret = 0;  
    while (ch < '0' || '9' < ch) ch = getchar();  
    while ('0' <= ch && ch <= '9')  
        ret = ret*10 + ch - '0',ch = getchar();  
    return ret;  
}  
    
int main()  
{  
    #ifdef DMC  
        freopen("DMC.txt","r",stdin);
    #endif  
        
    n = getint(); d = getint(); k = getint();
    for (int i = 1; i <= n; i++)  
        for (int j = 1; j <= d; j++)  
            a[i][j] = getint() % k;  
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= d; j++)
            g[i] += a[i][j]*a[i][j];
    for (int i = 1; i <= n; i++) g[i] %= k;
    if (k == 2) Solve2();  
    else Solve3();  
    return 0;  
}  

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