zoukankan      html  css  js  c++  java
  • BZOJ3243/UOJ121 [Noi2013]向量内积

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

    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<q
    )。如果存在多组这样的向量组合,输出其中任意一组即可。若不存在这样的向量组合,则输出两个-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
     

     

    正解:随机化+矩阵乘法+搜索

    解题报告:

      这道题非常有意思呀…

      首先如果把所有向量列在一起可以得到一个n*d的矩阵,而将这个矩阵转置得到一个转置矩阵,用矩阵乘转置矩阵,将得到的新矩阵。

      容易发现,新矩阵的第i行第j个数就是第i个向量和第j个向量的内积。

      如果在模2意义下,只要新矩阵中存在0,则说明存在是2的倍数的组合。

      而我们可以和全1矩阵进行比较。如果不相等则说明存在,暴力寻找;否则不存在。

      注意到为了支持快速判断两个大矩阵是否相等,我需要用一个另外的矩阵分别乘等式两边的矩阵,如果最终结果相同则视为两个矩阵相等。

      这样做有可能出错,多随几次提高判断正确的概率。

     

      对于k=3的情况,不能用上述做法做,考虑把结果平方一下,则可以把2化成1。考虑不用矩乘,直接用点积:

      考虑我先随机一个1到n的排列,每次用当前排列所代表的向量,去与之前的所有向量做点积。得到的答案平方之后,再加起来。

      那么我可以得到一个权值,如果为i-1则说明全为1,与全1矩阵相等。否则出现了0,暴力寻找,输出答案即可。

      考虑如何优化快速求与之前所有向量的点积的平方和。

      ${(sum_{i=1}^{d}a_i*b_i)^2}$这是a向量和b向量的点积的平方。

      ${(sum_{i=1}^{d}a_i*b_i)^2}=sum_{i=1}^{d}sum_{j=1}^{d}a_i*b_i*a_j*b_j$

      那么我令a为排列的第i个数所代表的向量,则令b为排列的前i-1个数的前缀和,则可快速求得。

     

    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    using namespace std;
    typedef long long LL;
    const int MAXN = 100011;
    const int MAXD = 102;
    int n,d,k,c[MAXN],ans[MAXN],c2[MAXN],q[MAXN];
    int a[MAXN][MAXD],b[MAXD][MAXN],tot,sum[MAXD][MAXD];//转置矩阵不要写反了!
    
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline int calc(int x){
    	int tot=0;
    	for(int i=1;i<=d;i++)
    		for(int j=1;j<=d;j++)
    			tot+=sum[i][j]*a[x][i]*a[x][j],sum[i][j]+=a[x][i]*a[x][j];
    	return tot%3;
    }
    
    inline void work(){
    	srand(20000605);
    	n=getint(); d=getint(); k=getint();
    	for(int i=1;i<=n;i++) for(int j=1;j<=d;j++) a[i][j]=getint(),a[i][j]%=k;
    	for(int j=1;j<=d;j++) for(int i=1;i<=n;i++) b[j][i]=a[i][j];
    	if(k==2) {
    		int Case=0;
    		while(Case<=5) {//随机几次
    			Case++; if(Case>5) break; tot=0;
    			for(int i=1;i<=n;i++) c[i]=rand()%k,tot+=c[i]; tot%=k; for(int i=1;i<=n;i++) ans[i]=0;
    			for(int i=1;i<=d;i++) { for(int j=1;j<=n;j++) ans[i]+=c[j]*a[j][i]; ans[i]%=k; }
    			for(int i=1;i<=n;i++) c2[i]=0;
    			for(int i=1;i<=n;i++){ for(int j=1;j<=d;j++) c2[i]+=ans[j]*b[j][i];	c2[i]%=k; }
    			int tag=-1;	for(int i=1;i<=n;i++) if(c2[i]!=tot) { tag=i; break; }//不同的位置
    			if(tag==-1) continue; int tag2=-1,now;
    			for(int i=1;i<=n;i++)  {
    				if(i==tag) continue;
    				now=0; for(int j=1;j<=d;j++) now+=a[tag][j]*b[j][i];
    				now%=k;	if(now==0) { tag2=i; break; }
    			}
    			if(tag2!=-1) { if(tag>tag2) swap(tag,tag2); printf("%d %d
    ",tag,tag2); return ; }
    		}
    		printf("-1 -1");
    	}
    	else{
    		for(int i=1;i<=n;i++) q[i]=i;
    		random_shuffle(q+1,q+n+1); 
    		int Case=1;
    		while(Case--) {
    			memset(sum,0,sizeof(sum));
    			for(int i=1;i<=n;i++) {
    				if(calc(q[i])!=((i-1)%3)) {
    					for(int j=1;j<i;j++) { 
    						int tot=0;
    						for(int l=1;l<=d;l++) tot+=a[q[i]][l]*a[q[j]][l];
    						if(tot%3==0) { printf("%d %d",min(q[i],q[j]),max(q[i],q[j])); return ; }
    					}
    				}
    			}
    		}
    		printf("-1 -1");
    	}
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    Leetcode 1489找到最小生成树李关键边和伪关键边
    Leetcode 113 路径总和 II
    hdu 1223 还是畅通工程
    hdu 1087 Super Jumping! Jumping! Jumping!
    hdu 1008 Elevator
    hdu 1037 Keep on Truckin'
    湖工oj 1241 畅通工程
    湖工oj 1162 大武汉局域网
    hdu 2057 A + B Again
    poj 2236 Wireless Network
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6388599.html
Copyright © 2011-2022 走看看