【题目链接】
【思路要点】
- 首先我们来考虑\(k=2\)的情况。
- 将所有向量拼成一个\(N\)行\(D\)列的矩阵\(A\),那么问题等价于找到\(A*A^{T}\)中的一个不在主对角线上的0,或者判断不存在这样的0。
- 显然,直接计算\(A*A^{T}\)会超时。
- 注意到如果找不到这样的零,\(A*A^{T}\)不在主对角线上的元素应该都是1,设矩阵\(B\)是一个\(N\)行\(N\)列的矩阵,它主对角线上的元素与\(A*A^{T}\)相同,其余元素都为1,我们希望知道\(A*A^{T}\)与\(B\)是否有不同,如果有,我们希望知道不同点处的至少一维坐标(另一维可以暴力求解)。
- 假设\(A*A^{T}=B\),那么根据矩阵乘法的性质,应当有\(X*A*A^{T}=X*B\),其中\(X\)是一个1行\(N\)列的矩阵。
- \(X*A*A^{T}\)是容易在\(O(ND)\)的时间里求解的,由于\(B\)中只有\(O(N)\)个1,\(X*B\)也是容易在\(O(N)\)的时间里求解的。
- 但满足\(X*A*A^{T}=X*B\)时,\(A*A^{T}=B\)是不一定成立的,因为所有的运算是在取模意义下进行的,因此,就像是哈希的模数一样,我们可以随机多个矩阵来判断\(A*A^{T}=B\)是否成立。
- 一旦发现\(A*A^{T}\)与\(B\)不相等,不相等的坐标处就是我们需要的一维坐标。
- 当\(k=3\)时,\(B\)中不在主对角线上的数可能是1或者2,难以直接建立矩阵。
- 注意到\(2^2\equiv 1^2\equiv 1\ (Mod\ 3)\),设新矩阵\(C\),满足\(C_{i,j}=B_{i,j}^2\),\(C\)矩阵是容易确定的。
- \(C_{i,j}=(\sum_{k=1}^{D}A_{i,k}A^{T}_{k,j})^2=\sum_{k=1}^{D}\sum_{l=1}^{D}A_{i,k}A_{i,l}*A^{T}_{k,j}A^{T}_{l,j}\)
- 把上面的\((k,l)\)看做是矩阵的下标,这就也可以看成矩阵乘法的形式了。
- 时间复杂度\(O(CntND^{k-1})\),其中\(Cnt\)为尝试随机次数。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXM = 105; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, P; int x[MAXN], y[MAXM][MAXM], z[MAXN], d[MAXN]; int a[MAXN][MAXM], b[MAXM][MAXN], dot[MAXN]; bool solve(int x) { for (int i = 1; i <= n; i++) { if (x == i) continue; int ans = 0; for (int j = 1; j <= m; j++) ans += a[x][j] * a[i][j]; if (ans % P == 0) { if (i > x) printf("%d %d\n", x, i); else printf("%d %d\n", i, x); return true; } } return false; } int main() { read(n), read(m), read(P); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { read(a[i][j]); a[i][j] %= P; b[j][i] = a[i][j]; } for (int i = 1; i <= n; i++) { int ans = 0; for (int j = 1; j <= m; j++) ans += a[i][j] * a[i][j]; dot[i] = ans % P != 0; } if (n <= 1e3) { for (int i = 1; i <= n; i++) if (solve(i)) return 0; printf("-1 -1\n"); return 0; } srand(1840); int cnt = 4; while (cnt--) { int sum = 0; for (int i = 1; i <= n; i++) { x[i] = rand() % 5 != 0; sum += x[i]; } for (int i = 1; i <= n; i++) d[i] = (sum - x[i] * (dot[i] ^ 1) + P) % P; memset(y, 0, sizeof(y)); memset(z, 0, sizeof(z)); if (P == 2) { for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) y[0][i] += x[j] * a[j][i]; y[0][i] %= P; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) z[i] += y[0][j] * b[j][i]; z[i] %= P; } } else { for (int i = 1; i <= m; i++) for (int j = 1; j <= m; j++) { for (int k = 1; k <= n; k++) y[i][j] += x[k] * a[k][i] * a[k][j]; y[i][j] %= P; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) for (int k = 1; k <= m; k++) z[i] += y[j][k] * b[j][i] * b[k][i]; z[i] %= P; } } for (int i = 1; i <= n; i++) if (z[i] != d[i]) { solve(i); return 0; } } printf("-1 -1\n"); return 0; }