本文版权归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\n",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;
}

  

BZOJ3243/UOJ121 [Noi2013]向量内积的更多相关文章

  1. 【fake题解】[NOI2013]向量内积

    [fake题解][NOI2013]向量内积 做法1 大暴力.哪里不会T哪里. 做法2 所有数都%=k不影响结果.(废话 k的取值只有2和3,所以肯定是要分类讨论的.k=2肯定简单些啦. k=2 出现的 ...

  2. [Noi2013]向量内积

    来自FallDream的博客,未经允许,请勿转载,谢谢. 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: $\sum_{i=1 ...

  3. P1224 [NOI2013]向量内积

    传送门 发现这个内积和矩乘有点像,考虑构造一个 $n$ 行 $m$ 列的矩阵 $A$,每一行都是一个题目给定的 $m$ 维向量 设 $B=AA^T$ ,其中 $A^T$ 为 $A$ 的转置矩阵,那么对 ...

  4. luogu P1224 [NOI2013]向量内积

    传送门 挺有意思的一道题 暴力60就是枚举每个向量暴力check,随机选向量就能多骗一些分 然后两个向量内积要模\(k\)为\(0\),那么如果全部不为\(0\)就不合法.先考虑\(k=2\),对于向 ...

  5. 【uoj121】 NOI2013—向量内积

    http://uoj.ac/problem/121 (题目链接) 题意 给出${n}$个${d}$维向量,问是否有两个不同的向量的内积是${k}$的倍数. Solution 又卡了一上午常数,我弃了T ...

  6. BZOJ3243 NOI2013向量内积(随机化)

    考虑奇技淫巧. 首先是k=2.对向量维护一个前缀和,每次将当前向量与前缀和点乘.如果点乘结果不等于i-1&1,说明当前向量至少和之前的某个向量的数量积是2的倍数,暴力找就可以了.当然等于i-1 ...

  7. BZOJ3243 [Noi2013]向量内积 【乱搞】

    题目链接 BZOJ3243 题解 模数只有\(2\)或\(3\),可以大力讨论 如果模数为\(2\),乘积结果只有\(1\)或\(0\) 如果一个向量和前面所有向量乘积都为\(1\),那么其和前面向量 ...

  8. 3243: [Noi2013]向量内积 - BZOJ

    Description 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: 现有 n 个d 维向量x1,...,xn ,小喵喵想知 ...

  9. bzoj 3243: [Noi2013]向量内积

    Description 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: 现有 n 个d 维向量x1,...,xn ,小喵喵想知 ...

随机推荐

  1. Java类的加载、链接和初始化(个人笔记)

    这里看到一篇比较好的文章:http://www.infoq.com/cn/articles/cf-Java-class-loader 这里只是针对什么时候会触发java类的初始化(注意:这里不是说的实 ...

  2. Linux下OpenOffice的安装与启动

    公司项目需求中增加了文档预览功能,所以采用了OpenOffice提供的将office文件转换为pdf的工具.那么我们的程序运行在服务器端,服务器系统版本多是Linux,因此有必要记录下Linux下Op ...

  3. java 多种判断key是否在map中存在的方法

    java 中有时候会遇到判断传过来的map里是否包含了指定的key,我目前只发现两种办法,如果有其他方法欢迎补充 我添加上去: HashMap map = new HashMap(); map.put ...

  4. Web开发之容器

    Web开发之容器 主题 Servlet容器.Web容器.应用服务器 参考资料   Servlet容器.Web容器.应用服务器         Servlet容器的主要任务是管理Servlet的生命周期 ...

  5. 2018.10.26-day5 python整理总结

    今日内容: 1.字典 2.id is == 3.小数据池 4.集合昨日回顾:1.列表:可变的 增:append//insert//extend//+//* 删:remove//pop//clear// ...

  6. Linux python3安装/shell脚本/if/循环/函数

    python3安装 安装过程 安装包: wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgztar -xvf Python-3.7 ...

  7. Python3.6全栈开发实例[011]

    11.元素分类有如下值li= [11,22,33,44,55,66,77,88,99,90],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中.即: {'k ...

  8. 印象笔记ipad端快捷键

  9. spring 登录提示 Bad credentials

    spring 日志输出:Authentication failed: password does not match stored value in spring security 3.2,检查密码发 ...

  10. Python基础-高阶函数

    1.高阶函数变量可以指向函数函数的参数可以接收变量一个函数可以接收另一个函数作为参数 def f(n): return n * n def fansik(a, b, func): return fun ...