题目描述
两个 d 维向量 A=[a1,a2,…,ad] 与 B=[b1,b2,…,bd] 的内积为其相对应维度的权值的乘积和,即:
现在有 n 个 d 维向量 x1,x2,…,xn,小喵喵想知道是否存在两个向量的内积为 k 的倍数。请帮助她解决这个问题。
输入格式
第一行包含 3 个正整数 n,d,k,分别表示向量的个数,维数以及待检测的倍数。
接下来 n 行每行有 d 个非负整数,其中第 i 行的第 j 个整数表示向量 xi 的第 j 维权值 xi,j。
输出格式
包含两个整数,用空格隔开。
如果存在两个向量 xp,xq 的内积为 k 的整数倍,则输出两个向量的编号 p 与 q (要求 p<q)。如果存在多组这样的向量组合,输出其中任意一组即可。
若不存在这样的向量组合,则输出两个 −1。
样例一
input
3 5 2 1 0 1 0 1 1 1 0 1 0 0 1 0 1 1
output
2 3
explanation
⟨x1,x2⟩=1,⟨x1,x3⟩=1,⟨x2,x3⟩=2。
限制与约定
测试点编号 | n | d | k | xi,j |
---|---|---|---|---|
1 | 2 | 20 | 2 | ≤10 |
2 | 5 | 20 | 2 | ≤10 |
3 | 10 | 20 | 3 | ≤10 |
4 | 20 | 20 | 2 | ≤100 |
5 | 50 | 20 | 3 | ≤100 |
6 | 50 | 50 | 2 | ≤1000 |
7 | 50 | 50 | 3 | ≤3000000 |
8 | 80 | 80 | 2 | ≤3000000 |
9 | 100 | 100 | 3 | ≤3000000 |
10 | 500 | 100 | 3 | ≤3000000 |
11 | 1000 | 100 | 2 | ≤3000000 |
12 | 1000 | 100 | 3 | ≤3000000 |
13 | 10000 | 100 | 2 | <10 |
14 | 10000 | 100 | 3 | <10 |
15 | 15000 | 100 | 2 | <10 |
16 | 18000 | 100 | 2 | <10 |
17 | 20000 | 100 | 2 | <10 |
18 | 50000 | 30 | 3 | <10 |
19 | 80000 | 30 | 3 | <10 |
20 | 100000 | 30 | 3 | <10 |
时间限制:5s
空间限制:256MB
分析
注意:以下运算都在模k意义下
k=2
我们把这些向量看成一个n×d的矩阵A
A的转置矩阵 AT=⎡⎣⎢⎢⎢⎢⎢x1,1x1,2⋮x1,nx2,1x2,2⋮x2,nx3,1x3,2⋮x3,n⋯⋯⋱⋯xn,1xn,2⋮xnn⎤⎦⎥⎥⎥⎥⎥
令B=A×AT,那么bi,j就表示xi⋅xj,如果B矩阵除了主对角线全部是1的话,显然无解。
但是求出B矩阵的时间显然是O(n2d),显然是无法通过本题的。
我们令B′为bi,i=xi⋅xi,其余元素为1的矩阵。
如果B=B′的话,就无解。
我们再随机找出一个列向量C (其实就是全是1,因为0没用。。。)
D=AT×C我们可以用O(nd)的时间求出来,A×D也可以用O(nd)的时间求出来,B′×C可以O(n)求出。
然后比较A×(AT×C) B′×C这两个列向量的每一个元素是否相同,如果第i行的元素不相同,说明xi肯定是答案只要,枚举找出xj即可。
尽管有出错的概率,但是这个算法还是比较可靠的。
k=3
我们发现
所以,我们令 B=(A∗AT)2其余矩阵定义不变,那么展开后会发现
然后参考 k=2的算法即可,由于 k=3时矩阵太大,内存开不下,但是算的时候按照规则算就行了。
代码
#include
#include
using namespace std;
#define MAXN 100000
#define MAXM 100
int n,d,k,a[MAXN+10][MAXM+10],s[MAXN+10],c[MAXM*MAXM+10],g[MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
int Get_id(int i,int j){
return (i-1)*d+j;
}
void read(){
Read(n),Read(d),Read(k);
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=d;j++){
Read(a[i][j]);
a[i][j]%=k;
s[i]+=a[i][j]*a[i][j];
}
for(i=1;i<=n;i++)
s[i]%=k;
}
void solve2(){
int i,j,k,sum;
for(i=1;i<=d;i++)
for(j=1;j<=n;j++)
c[i]^=a[j][i];
for(i=1;i<=n;i++)
for(j=1;j<=d;j++)
g[i]^=a[i][j]&c[j];
for(i=1;i<=n;i++)
if(g[i]!=(s[i]^((n-1)&1)))
break;
if(i>n){
puts("-1 -1");
return;
}
for(j=1;j<=n;j++)
if(i!=j){
sum=0;
for(k=1;k<=d;k++)
sum^=a[i][k]&a[j][k];
if(!sum)
break;
}
if(i>j)
swap(i,j);
printf("%d %d\n",i,j);
}
void solve3(){
int i,j,k,sum;
for(i=1;i<=d;i++)
for(j=1;j<=d;j++)
for(k=1;k<=n;k++)
c[Get_id(i,j)]+=a[k][i]*a[k][j];
for(i=d*d;i;i--)
c[i]%=3;
for(i=1;i<=n;i++)
for(j=1;j<=d;j++)
for(k=1;k<=d;k++)
g[i]+=a[i][j]*a[i][k]*c[Get_id(j,k)];
for(i=1;i<=n;i++)
if(g[i]%3!=((n-1)+s[i]*s[i])%3)
break;
if(i>n){
puts("-1 -1");
return;
}
for(j=1;j<=n;j++)
if(j!=i){
sum=0;
for(k=1;k<=d;k++)
sum=(sum+a[i][k]*a[j][k])%3;
if(!sum)
break;
}
if(i>j)
swap(i,j);
printf("%d %d\n",i,j);
}
int main()
{
read();
if(k==2)
solve2();
else
solve3();
}