Solution -「NOI2013」向量内积
学不懂线性代数怎么办((
考虑把 \(n\) 个向量归入一个矩阵 \(A\)。根据题意,在模 \(k\) 意义下,\(B = (A \cdot A^T)^2, B_{i, j} = \vec{a}_i\vec{a}_j^T\) 是一个 0/1
矩阵。
不难发现,如果 \(B\) 是一个全 \(1\) 矩阵则说明不存在符合题意的一组向量。否则如果 \(B_{i, j} = 0\) 则 \((i, j)\) 是符合条件的一组解。
\(O(n^2d)\) 这咋判。
考虑随机一个向量的顺序。对于向量 \(\vec{a}_i\) 求其与 \(\vec{a}_j,j < i\) 内积的平方之和,如果所有内积在模 \(k\) 意义下均不等于 \(0\),则该和为 \((i - 1) \bmod k\)。
即,如果该和等于 \((i - 1) \bmod k\),则有概率之前没有向量可以与 \(\vec{a_i}\) 配对。否则之前一定存在一个向量可以与 \(\vec{a_i}\) 配对,暴力 Check。
展开吧。
\[\sum _{j = 1} ^{i - 1} (\vec{a}_i\vec{a}_j^T)^2 = \sum _{j = 1} ^{i - 1} \sum _{x = 1} ^{d} \sum _{y = 1} ^{d} a_{i, x} a_{j, x} a_{i, y} a_{j, y} = \sum _{x = 1} ^{d} \sum _{y = 1} ^{d}a_{i, x}a_{i, y} \sum _{j = 1} ^{i - 1} a_{j, x}a_{j, y}
\]
#include <cstdio>
#include <algorithm>
using namespace std;
int Abs (int x) { return x < 0 ? -x : x; }
int Max (int x, int y) { return x > y ? x : y; }
int Min (int x, int y) { return x < y ? x : y; }
int Read () {
int x = 0, k = 1;
char s = getchar ();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar ();
}
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}
void Write (int x) {
if (x < 0)
putchar ('-'), x = -x;
if (x > 9)
Write (x / 10);
putchar (x % 10 + '0');
}
void Print (int x, char s) { Write (x), putchar (s); }
const int Maxn = 1e5 + 5;
const int Maxm = 1e2 + 5;
int a[Maxn][Maxm], Sum[Maxn][Maxm], p[Maxn], n, d, k;
int Calc (int x) {
int Res = 0;
for (int i = 1; i <= d; i++)
for (int j = 1; j <= d; j++)
Res = (Res + a[x][i] * a[x][j] * Sum[i][j]) % k, Sum[i][j] = (Sum[i][j] + a[x][i] * a[x][j]) % k;
return Res;
}
int Mul (int x, int y) {
int Res = 0;
for (int i = 1; i <= d; i++)
Res = (Res + a[x][i] * a[y][i]) % k;
return Res;
}
int main () {
n = Read (), d = Read (), k = Read ();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= d; j++)
a[i][j] = Read (), a[i][j] %= k;
for (int i = 1; i <= n; i++)
p[i] = i;
for (int t = 1; t <= 7; t++) {
random_shuffle (p + 1, p + n + 1);
for (int i = 1; i <= d; i++)
for (int j = 1; j <= d; j++)
Sum[i][j] = 0;
for (int i = 1; i <= n; i++)
if (Calc (p[i]) != (i - 1) % k)
for (int j = 1; j < i; j++)
if (!Mul (p[i], p[j])) {
Print (Min (p[i], p[j]), ' '), Print (Max (p[i], p[j]), '\n');
return 0;
}
}
Print (-1, ' '), Print (-1, '\n');
return 0;
}