题目
解法
观察数据,我们发现 (k) 只有 (2,3) 两种情况,这提醒我们分类讨论。
( ext{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))。
( ext{case}_2:k=3)
因为 (Y) 最终可能有 (0,1,2) 三种值,上面的方法就行不通了。
然后有个很妙的转化:设 (Z_{i,j}=Y_{i,j}^2pmod 3)。这样就又变成了 (0,1) 两种值。
问题在于,(Z) 是 (Y) 单项的平方,并不能直接矩乘获得,所以尝试推一推式子((alpha) 是随机的向量):
[(Zalpha)_i=sum_{j=1}^nZ_{i,j}alpha_j
]
[=sum_{j=1}^nY_{i,j}^2alpha_j
]
[=sum_{j=1}^nalpha_jleft(sum_{k=1}^d A_{i,k}A^T_{k,j}
ight)^2
]
这时可以做到 (mathcal O(n^2d)) 的复杂度,但它和暴力复杂度是一样的。可以尝试抛开与 (i) 无关的项然后进行预处理。
[=sum_{j=1}^nalpha_jleft(sum_{k_1=1}^d A_{i,k_1}A_{j,k_1}
ight)left(sum_{k_2=1}^d A_{i,k_2}A_{j,k_2}
ight)
]
[=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),来判断 ((Zalpha)_i) 与 ((Ealpha)_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
",min(pos,i),max(pos,i));
break;
}
}
}
return 0;
}