题目
思路
这题如果直接从线性代数的角度去理解,还真的挺困难的。然后越思考越觉得有更通俗的理解。不如从 k = 2 k=2 k=2 开始做。
首先,我们需要想到这样一个东西:多个向量与多个向量,两两求点积,这实际上是 矩阵乘法,行向量乘列向量。
那么,将所有行向量拼成一个矩阵 A A A,我们只需要求出 A A T AA^T AAT 中为 0 0 0 的位置。不,不应该是求出,而是找到任意一个。显然这个运算是在模 2 2 2 意义下进行的。
那么我们想起这道题,试着搬到这边。啥情况无解呢?就是矩阵 B B B,满足 B i , i = x i ⋅ x i B_{i,i}={\bf x}_i\cdot{\bf x}_i Bi,i=xi⋅xi,其余位置均为 1 1 1 嘛。
随机一个列向量 C C C,来判断 A ( A T C ) A(A^TC) A(ATC) 是不是等于 B B B 。如果不等于 B B B,哪里是不同的位置呢?假如是 B i , j B_{i,j} Bi,j 不匹配,乘一个 C j C_{j} Cj 就贡献到了第 i i i 位。即,最终得到的列向量是第 i i i 位不同,那必定是第 i i i 行的某个值为 0 0 0,那么 x i {\bf x}_i xi 就可以被锁定,枚举另一个即可。
虽然 B B B 矩阵是 n × n n\times n n×n 的,无法求出,但是因为它很特殊, B × C B\times C B×C 可以直接算。时间复杂度 O ( n d ) \mathcal O(nd) O(nd) 。
然后 k = 3 k=3 k=3 又咋整呢?好像无法唯一确定 B B B 了……但是 1 2 ≡ 2 2 ≡ 1 ( m o d 3 ) 1^2\equiv 2^2\equiv 1\pmod{3} 12≡22≡1(mod3),所以我们可以考虑把它平方。怎么把 B i , j B_{i,j} Bi,j 平方呢?竟然还得自己再写式子,差评!
最终的结果的第
x
x
x 位是
∑
i
C
i
⋅
(
∑
j
A
x
,
j
A
j
,
i
T
)
2
=
∑
j
1
,
j
2
A
x
,
j
1
A
x
,
j
2
∑
i
C
i
A
j
1
,
i
T
A
j
2
,
i
T
\sum_{i}C_i\cdot \left(\sum_{j}A_{x,j}A^T_{j,i}\right)^2\\ =\sum_{j_1,j_2}A_{x,j_1}A_{x,j_2}\sum_{i}C_iA^T_{j_1,i}A^T_{j_2,i}
i∑Ci⋅(j∑Ax,jAj,iT)2=j1,j2∑Ax,j1Ax,j2i∑CiAj1,iTAj2,iT
于是我们要预处理后面那个求和式,记为 f ( j 1 , j 2 ) f(j_1,j_2) f(j1,j2),然后每个 x x x 暴力算就行。时间复杂度 O ( n d 2 ) \mathcal O(nd^2) O(nd2) 。
这时候,我们回头望月,感觉这东西似乎有别的理解。 a ⋅ b + a ⋅ c = a ⋅ ( b + c ) \bf a\cdot b+a\cdot c=a\cdot(b+c) a⋅b+a⋅c=a⋅(b+c),这是点积的分配率。那么如果 a \bf a a 和 r r r 个向量的点积都是 1 1 1,那么 a \bf a a 和这 r r r 个向量的和的点积应该是 r m o d 2 r\bmod 2 rmod2 才对。我们随机一个列向量,本质就是随机一个集合,然后求出这个集合内的向量之和,然后与每个其他向量求点积,看是否符合标准。
所以我们必须多随机几次——如果恰好有偶数个 0 0 0,还是不能检测出错误。当然我们顺便求出了正确的概率:选择偶数个 0 0 0 和选择奇数个 0 0 0 的方案数相同,如果每次在所有方案中随机,正确率应该是 50 % 50\% 50% 。当然这是针对某一个向量而言的,多个向量同时作用的话,概率只会更高。
再看看 k = 3 k=3 k=3 的情况呢?不是简单的分配率了。但其实也简单,点积的平方嘛,就是在两个维度上都对应相乘,即 a i a j b i b j {\bf a}_i{\bf a}_j{\bf b}_i{\bf b}_j aiajbibj,仿照一次方时要记录 i i i,这里我们记录 i , j i,j i,j 。同样的,每个向量要分配一个系数,然后求和,再跟每个向量求 “点积”,看看是否能够匹配全 1 1 1 的值。
这里的 C i C_i Ci 可以在 { 0 , 1 , 2 } \{0,1,2\} {0,1,2} 中取值,可能稍微好点?这个概率比较难算。但就算仍然用 { 0 , 1 } \{0,1\} {0,1},概率也还是上面的 50 % 50\% 50%,靠谱。
代码
这个代码其实有一个小问题:当 n n n 很小时,比如 n = 3 n=3 n=3 时,由于用的是 r a n d o m s h u f f l e \rm random\;shuffle randomshuffle 而不是重新 r a n d \rm rand rand,会难以检测出问题。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x > 9) writeint(x/10);
putchar((x-x/10*10)^48);
}
int Mod, **vec, n, d;
int C[100000]; // random value
int tmp1[100], tmp2[100][100];
int self[100000]; // x_i.dot(x_i)
int check(){
int sum = 0;
rep(i,0,n-1) sum += C[i];
if(Mod == 2){
memset(tmp1,0,d<<2);
rep(i,0,n-1) if(C[i]) rep(j,0,d-1)
tmp1[j] ^= vec[i][j];
for(int i=0,got; i<n; ++i){
int want = sum+C[i]*(self[i]-1);
rep(j,got=0,d-1)
got ^= (tmp1[j]&vec[i][j]);
if((want&1) != got) return i;
}
}
else if(Mod == 3){
rep(i,0,d-1) memset(tmp2[i],0,d<<2);
rep(i,0,n-1) if(C[i])
rep(j,0,d-1) if(vec[i][j])
rep(k,0,d-1) // two dimensions
tmp2[j][k] += vec[i][j]
*vec[i][k]*C[i];
rep(j,0,d-1) rep(k,0,d-1)
tmp2[j][k] %= 3; // %= Mod
for(int i=0,got; i<n; ++i){
int want = sum+C[i]*(
self[i]*self[i]-1);
want = (want%3+3)%3;
rep(j,got=0,d-1) rep(k,0,d-1)
got += tmp2[j][k]*
vec[i][j]*vec[i][k];
if(want != got%3) return i;
}
}
return -1;
}
int main(){
n = readint(), d = readint();
Mod = readint();
vec = new int*[n];
rep(i,0,n-1){
vec[i] = new int[d];
rep(j,0,d-1){
vec[i][j] = readint()%Mod;
self[i] += vec[i][j]*vec[i][j];
}
self[i] %= Mod;
}
int id = -1, sy = 10;
for(; !(~id)&&sy; --sy){
rep(i,0,n-1) C[i] = rand()&1;
id = check();
}
if(id == -1) puts("-1 -1");
else{
for(int i=0,got; i<n; ++i){
if(i == id) continue;
rep(j,got=0,d-1)
got += vec[id][j]*vec[i][j];
if(got%Mod == 0){
writeint(min(id,i)+1);
putchar(' ');
writeint(max(id,i)+1);
break; // done
}
}
putchar('\n');
}
return 0;
}
后记
我原本想的就是随机一个集合。比较类似 P o l l a r d − R h o \rm Pollard-Rho Pollard−Rho 里面,将多个数乘在一起再求 gcd \gcd gcd 。
总结一下,随机化无非就是:找必要条件,然后让它尽可能充分,方法是随机化;或者寻找某个频率较大的特征值(一般是 50 % 50\% 50%,例如此题),利用随机化和 c h e c k \rm check check 。

本文通过通俗解释,探讨了如何利用随机化方法解决涉及向量点积的线性代数问题,如找到矩阵中特定元素为0的位置。通过矩阵乘法和模2运算,逐步解析了k=2和k=3的解题思路,并揭示了随机选择集合的重要性。
590

被折叠的 条评论
为什么被折叠?



