码迷,mamicode.com
首页 > 其他好文 > 详细

[Noi2013]向量内积

时间:2017-04-29 15:07:29      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:reg   stdin   允许   存在   等于   class   oid   jpg   geo   

来自FallDream的博客,未经允许,请勿转载,谢谢。


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

技术分享

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

k=2时 n<=20000 d<=100  k=3时n<=1000,d<=100 或者n<=100000 d<=30

 

把两个向量内积看作矩阵一个1*d的矩阵和d*1的矩阵相乘

那么k=2的时候,就是一个n*d的矩阵和一个d*n的矩阵相乘,问得到的n*n的矩阵除了主对角线之外有没有0(mod k)

直接计算复杂度n^2d 所以不考虑计算它,而是转而判断得到的矩阵是否等于我想要的矩阵

假设n*d的矩阵为A,把它倒过来之后得到的d*n的矩阵为B,C=A*B,我期望的矩阵为D

矩阵D的主对角线的值可以O(nd)计算,其他值显然是1。

要判断C是否等于D,我可以转而随机一个n*1的向量T,判断C*T是否等于D*T即可

因为T只存在0和1,所以D*T可快速计算 而计算A*B*T时,我们可以先计算B*T,再计算A*(B*T),这两次计算都是O(nd)的

那么就做完了。复杂度O(nd)

 

但是k=3的时候,无法直接知道期望的矩阵,但是发现2^2=1(mod 3)

于是考虑计算E,Eij=Cij^2;矩阵D主对角线也对应平方一下 

发现$Eij=(\sum Aik*Bkj)^{2}$ 展开后得到$Eij=\sum_{k1=1}^{d}\sum_{k2=1}^{d}Aik1*Bk1j*Aik2*Bk2j$

这个可以看作$n*d^{2}$的矩阵和一个$d^{2}*n$的矩阵相乘 之后就按照k=2的做法就行了 复杂度$O(nd^{2})$

 

但是这个办法并不能保证正确 如果直接让矩阵T都是1的话 在bzoj会被叉...

所以我们多随机几次就行了 一般都能过的吧

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rint register int
#define getchar() (*S++)
char BB[1<<26],*S=BB;
using namespace std;
inline 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;
}

int A[100005][105],B[100005],C[100005],D[100005],E[100005],n,d,K,cnt=0,sum=0,id[105][105];

inline bool check(int a,int b)
{
    int Sum=0;
    for(rint i=1;i<=d;++i)
        Sum+=A[a][i]*A[b][i];
    return Sum%K==0;
}

inline void Solve(int x)
{
    for(rint i=1;i<=n;++i)
        if(i!=x&&check(i,x))
            {printf("%d %d\n",min(x,i),max(x,i));return;}
}

int main()
{
    fread(BB,1,1<<26,stdin);
    srand(23333U);
    n=read();d=read();K=read();
    for(rint i=1;i<=n;++i)
        for(rint j=1;j<=d;++j)
            A[i][j]=read()%K;
    if(K==2) for(int It=1;It<=5;++It)
    {
        for(rint i=1;i<=n;++i) C[i]=rand()&1,sum+=C[i];
        for(rint i=1;i<=n;++i)
            for(rint j=1;j<=d;++j)
                B[j]+=A[i][j]*C[i];
        for(rint i=1;i<=d;++i) B[i]%=K;
        for(rint i=1;i<=n;++i)
            for(rint j=1;j<=d;++j)
                D[i]+=A[i][j]*B[j];
        for(rint i=1;i<=n;++i)
            for(rint j=1;j<=d;++j)
                E[i]+=A[i][j];
        for(rint i=1;i<=n;++i) E[i]=(E[i]*C[i]+sum-C[i])%K;
        for(rint i=1;i<=n;++i) if(E[i]!=(D[i]%K)) return Solve(i),0;
        sum=0;
        for(rint i=1;i<=n;++i) E[i]=D[i]=B[i]=0;
    }
    else for(int It=1;It<=5;++It)
    {
        for(rint i=1;i<=n;++i) C[i]=rand()%3,sum+=C[i];
        for(rint i=1;i<=d;++i)
            for(rint j=1;j<=d;++j)
                id[i][j]=++cnt;
        for(rint k=1;k<=n;++k)
            for(rint i=1;i<=d;++i)
                for(rint j=1;j<=d;++j)
                    B[id[i][j]]+=A[k][i]*A[k][j]*C[k];
        for(rint i=1;i<=cnt;i++) B[i]%=K;
        for(rint i=1;i<=n;++i)
            for(rint j=1;j<=d;++j)
                for(rint k=1;k<=d;++k)
                    D[i]+=A[i][j]*A[i][k]*B[id[j][k]];
        for(rint i=1;i<=n;++i)
            for(rint j=1;j<=d;++j)
                E[i]+=A[i][j]*A[i][j];
        for(rint i=1;i<=n;++i) E[i]%=K,E[i]=(E[i]*E[i]*C[i]+sum-C[i])%K;
        for(rint i=1;i<=n;++i) if(E[i]!=(D[i]%K)) return Solve(i),0;
        sum=0;
        for(rint i=1;i<=n;++i) E[i]=D[i]=B[i]=0;
    }
    return 0*puts("-1 -1");
}

[Noi2013]向量内积

标签:reg   stdin   允许   存在   等于   class   oid   jpg   geo   

原文地址:http://www.cnblogs.com/FallDream/p/noi2013d1t1.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!