BZOJ3243 [Noi2013]向量内积/UOJ121

本文版权归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;
}

  

时间: 2024-10-03 21:54:13

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

3243: [Noi2013]向量内积 - BZOJ

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.Output 包含两个整数,用空格隔开.如果存在两个向量xp,xq的内积

[Noi2013]向量内积

来自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

【uoj121】 NOI2013—向量内积

http://uoj.ac/problem/121 (题目链接) 题意 给出${n}$个${d}$维向量,问是否有两个不同的向量的内积是${k}$的倍数. Solution 又卡了一上午常数,我弃了T_T. 右转题解→_→:llg 代码 // uoj121 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #inc

[BZOJ]3243 向量内积(Noi2016)

小C做了之后很有感觉的题目之一,但因为姿势不对调了很久. 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<=1

编程题-最小向量内积-(1)

有两个向量v1=(x1,x2,...,xn)和v2=(y1,y2,...,yn),允许任意交换v1和v2各自的分量的顺序,计算v1和v2的内积x1y1+x2y2+...+xnyn的最小值 样例: 输入: n=3 v1=(1,3,?5) v2=(?2,4,1) 输出: -25 ( 令v1=(?5,1,3),v2=(4,1,?2) ) 首先我第一感觉就是,要保证最小, 1.如果在有正有负的情况下,那么有最大的正数乘以最小的负数, 2.如果都是正数,用最大的正数乘以最小的正数, 3.如果都是负数,用最

向量内积&amp;外积

一.向量的内积 1.1向量内积的定义 概括地说,向量的内积(点乘/点积/数量积)就是对两个向量执行点乘运算,即对这两个向量对应位一一相乘之后求和的操作,如下所示,对于向量a和向量b:                  a和b的点积公式为: 这里要求一维向量a和向量b的行列数相同.注意:点乘的结果是一个标量(数量而不是向量). 定义:两个向量a与b的内积为 a·b = |a||b|cos∠(a, b),特别地,0·a =a·0 = 0:若a,b是非零向量,则a与b正交的充要条件是a·b = 0.

NOI2015 UOJ122 向量内积

神题...... 还是大神讲得比较清晰~orz http://dffxtz.logdown.com/posts/197950-noi2013-vector-inner-product 启发题:poj3318 #include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #incl

向量内积的推导

基本式 几何 對稱性: . 線性函數:設 .固定  時, 而且 同樣道理,固定  時,

向量的内积和外积

向量的内积(点乘) 定义 概括地说,向量的内积(点乘/数量积).对两个向量执行点乘运算,就是对这两个向量对应位一一相乘之后求和的操作,如下所示,对于向量a和向量b:   a和b的点积公式为: 这里要求一维向量a和向量b的行列数相同.注意:点乘的结果是一个标量(数量而不是向量) 定义:两个向量a与b的内积为 a·b = |a||b|cos∠(a, b),特别地,0·a =a·0 = 0:若a,b是非零向量,则a与b****正交的充要条件是a·b = 0. 向量内积的性质: a^2 ≥ 0:当a^2