题目

传送门

解法

观察数据,我们发现 \(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;
}
posted on 2021-07-20 21:04  Oxide  阅读(108)  评论(0编辑  收藏  举报