题目:
题目链接:向量内积
题解:
在源神的大力相助以及题解的帮助之下,我这个菜鸡终于懂了,太不容易了,想思路想了将近两个小时,太菜了(〃>_<;〃)
还是想先手推一下样例:
1 0 1 0 1
1 1 0 1 0
0 1 0 1 1
则內积就为:1×0 + 1×1 + 0×0 + 1×1 + 0×1=2 % 2=0
所以,我们就需要从所有行中找到某二或三行中找到內积%k=0即可。
这样的话,由题意可知,A·AT是一个01矩阵,我们就设一个全1的矩阵C,则A·AT=C,我们再设一个1×n的矩阵(一行矩阵)S,则,S×(A·AT)=S×C,这样的话,我们就可以很容易的算出来S×C,再对S×(A·AT)进行展开优化即可。
但是,当k=3时该怎么办呢?
当k=3,的时候,这就不在是一个01矩阵了,这就是一个012的矩阵了,但是我们只需要记录下来0的可能就行,所以这该怎么办呢?
可以考虑一下, 2 x ≡ 1 ( m o d 3 ) 2^x≡1(mod 3) 2x≡1(mod3),那么x就肯定是2了,所以,我们就可以把情况转化为:将A,AT两者相乘之后再平方一下再%3,这样的话就可以完全避免掉012的矩阵,从而将012矩阵转化为01矩阵。这样就转化成了上面的形式。
说一下平方后的通项公式:
A
×
A
T
=
S
1
=
∑
k
=
1
d
a
i
k
⋅
a
j
k
A×A^T=S_1=\sum_{k=1}^da_{ik}·a_{jk}
A×AT=S1=k=1∑daik⋅ajk
S
1
2
=
∑
k
1
=
1
d
∑
k
2
=
1
d
a
i
k
1
⋅
a
j
k
1
⋅
a
i
k
2
⋅
a
j
k
2
S_1^2=\sum_{k_1=1}^d\sum_{k_2=1}^da_{ik_1}·a_{jk_1}·a_{ik_2}·a_{jk_2}
S12=k1=1∑dk2=1∑daik1⋅ajk1⋅aik2⋅ajk2
这样就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int sea=100020;
int a[sea][110],n,m,k;
int S1[sea],S2[sea][110],id[sea];
inline int read()
{
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int judge(int x,int y)//匹配
{
int s=0;
for(int i=1;i<=m;i++) s+=a[x][i]*a[y][i];
return (s%k==0);
}
int work(int x)
{
int ans=0;
if(k==2) //O(d)的匹配
for(int i=1;i<=m;S1[i]^=a[x][i],i++) ans^=(S1[i]&a[x][i]);
else //O(d^2)的匹配
for(int i=1;i<=m;i++)
for(int j=1;j<=m;S2[i][j]+=a[x][i]*a[x][j],j++)
ans+=S2[i][j]*a[x][i]*a[x][j];//自己求通项
return ans%k;
}
int main()
{
n=read(); m=read(); k=read();
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read()%k;
for(int i=1;i<=n;i++) id[i]=i;
int T=7;
while(T--)
{
memset(S1,0,sizeof(S1)); memset(S2,0,sizeof(S2));
random_shuffle(id+1,id+n+1);//随机数
for(int i=1;i<=n;i++) if(work(id[i])!=(i-1)%k)
for(int j=1;j<i;j++)
if(judge(id[i],id[j]))
{
if(id[i]>id[j]) swap(i,j);
printf("%d %d\n",id[i],id[j]);
return 0;
}
}
puts("-1 -1");
return 0;
}