题目
解法
观察数据,我们发现 \(k\) 只有 \(2,3\) 两种情况,这提醒我们分类讨论。
\(\text{case}_1:k=2\)
不妨将两两向量内积化成一个矩阵:设 \(n\) 个 \(d\) 维行向量组成的矩阵为 \(A\),那么这个矩阵为 \(Y=AA^T\)。
对于 \(k=2\),我们只需判断 \(Y\) 是否等于 \(E\)(\(E\) 是全 \(1\) 矩阵)。
用类似 这道题 的方法来判断即可。在判断时,可以用 \(\mathcal O(nd)\) 内找出哪个行向量 \(pos\) 有 \(0\)。然后我们再枚举其它向量,暴力计算它与 \(pos\) 的内积即可。
总时间复杂度 \(\mathcal O(nd)\)。
\(\text{case}_2:k=3\)
因为 \(Y\) 最终可能有 \(0,1,2\) 三种值,上面的方法就行不通了。
然后有个很妙的转化:设 \(Z_{i,j}=Y_{i,j}^2\pmod 3\)。这样就又变成了 \(0,1\) 两种值。
问题在于,\(Z\) 是 \(Y\) 单项的平方,并不能直接矩乘获得,所以尝试推一推式子(\(\alpha\) 是随机的向量):
\[(Z\alpha)_i=\sum_{j=1}^nZ_{i,j}\alpha_j
\]
\[=\sum_{j=1}^nY_{i,j}^2\alpha_j
\]
\[=\sum_{j=1}^n\alpha_j\left(\sum_{k=1}^d A_{i,k}A^T_{k,j} \right)^2
\]
这时可以做到 \(\mathcal O(n^2d)\) 的复杂度,但它和暴力复杂度是一样的。可以尝试抛开与 \(i\) 无关的项然后进行预处理。
\[=\sum_{j=1}^n\alpha_j\left(\sum_{k_1=1}^d A_{i,k_1}A_{j,k_1} \right)\left(\sum_{k_2=1}^d A_{i,k_2}A_{j,k_2} \right)
\]
\[=\sum_{k_1=1}^d \sum_{k_2=1}^d A_{i,k_1}A_{i,k_2}\sum_{j=1}^n \alpha_jA_{j,k_1}A_{j,k_2}
\]
后面一坨可以 \(\mathcal O(nd^2)\) 预处理成函数 \(g(k_1,k_2)\)。因为还要枚举 \(i\),来判断 \((Z\alpha)_i\) 与 \((E\alpha)_i\) 是否相等,也是 \(\mathcal O(nd^2)\) 的。
我随机了 \(5\) 次,类似 这道题 的方法,得出错误概率为 \(\frac{1}{2^5}\)(感觉好奇怪)。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <cstdlib>
#include <iostream>
using namespace std;
const int maxn=1e5+5,maxd=105;
int n,d,k,v[maxn][maxd],sum;
int a[maxd],r[maxn],pos;
int g[maxd][maxd];
bool flag;
void calc_2() {
for(int i=1;i<=d;++i) a[i]=0;
for(int i=1;i<=d;++i)
for(int j=1;j<=n;++j)
a[i]=(a[i]+v[j][i]*r[j])%k;
for(int i=1;i<=n;++i) {
int tmp=0;
for(int j=1;j<=d;++j)
tmp=(tmp+a[j]*v[i][j])%k;
if(tmp!=sum) {
flag=1,pos=i;
break;
}
}
}
void calc_3() {
for(int k1=1;k1<=d;++k1)
for(int k2=1;k2<=d;++k2) {
g[k1][k2]=0;
for(int j=1;j<=n;++j)
g[k1][k2]=(g[k1][k2]+
v[j][k1]*v[j][k2]*r[j])%k;
}
for(int i=1;i<=n;++i) {
int tmp=0;
for(int k1=1;k1<=d;++k1)
for(int k2=1;k2<=d;++k2)
tmp+=v[i][k1]*v[i][k2]*g[k1][k2];
if(tmp%k!=sum) {
flag=1,pos=i;
break;
}
}
}
int main() {
srand(12242013);
n=read(9),d=read(9),k=read(9);
for(int i=1;i<=n;++i)
for(int j=1;j<=d;++j)
v[i][j]=read(9)%k;
for(int T=1;T<=5;++T) {
sum=0;
for(int i=1;i<=n;++i)
r[i]=rand()%k,sum=(sum+r[i])%k;
if(k==2) calc_2();
else calc_3();
if(flag) break;
}
if(!flag) puts("-1 -1");
else {
for(int i=1;i<=n;++i)
if(i^pos) {
int tmp=0;
for(int j=1;j<=d;++j)
tmp+=v[pos][j]*v[i][j];
if(tmp%k==0) {
printf("%d %d\n",min(pos,i),max(pos,i));
break;
}
}
}
return 0;
}