【NOI2013】向量内积
题面
https://www.luogu.org/problem/P1224
题解
窝本来想用这道题写矩阵哈希的,所谓矩阵哈希,就是快速判断两个矩阵的乘积是不是另外一个矩阵。
矩阵哈希就是随机一个向量,利用矩阵结合律先算向量和各矩阵的乘积,然后直接比较两个向量是不是相等的。
这样本来是$n^3$的,就变成$n^2$的。
但是看到题解第一种方法更简便而且更易懂,就写第一种方法了。
所谓内积,就是点乘。
首先是二维的情况,假设检查到$i$个,我们检查$i$和$1..i-1$的前缀和的内积是否为$(i-1)\% 2$,如果不是,一定说明$i$和前面的某个$x$的内积是2的倍数,遍历$1~i-1$检查一遍。否则,不知道有没有,就不检查了。
三维的情况不能直接套用,因为如果内积不是$3$的倍数,不知道具体是余$1$还是余$2$,但是我们知道,不管是$1$还是$2$,他们的平方都是余$1$的(记得当年在奥数班,南小大聚聚$xjt$讲的结论),所以我们考虑求的形式$$(a_1b_1+a_2b_2+...+a_kb_k)^2=a_1b_1\times(a_1b_1+....+a_kb_k)+....+a_kb_k\times(a_1b_1+....+a_kb_k)$$,所以拆成$k^2$维的向量(或者说,拆成$k\times k$的“矩阵”)
然后很寒掺,只有$85$分,最后$3$个点$T$了,我也不知道怎么回事啊。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #define ri register int #define N 100050 #define D 105 using namespace std; int n,d,k; int v[N][D]; int b[D],c[D][D]; int id[N]; inline int read() { register char ch=getchar(); ri ret=0; while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0' && ch<='9') ret=ret*10+(ch-'0'),ch=getchar(); return ret; } inline int work(int cur) { ri ret=0; if (k==2) { for (ri i=1;i<=d;i++) ret+=b[i]*v[id[cur]][i]; for (ri i=1;i<=d;i++) b[i]+=v[id[cur]][i],b[i]%=2; } else { for (ri i=1;i<=d;i++) for (ri j=1;j<=d;j++) ret+=c[i][j]*v[id[cur]][i]*v[id[cur]][j]; for (ri i=1;i<=d;i++) for (ri j=1;j<=d;j++) c[i][j]+=v[id[cur]][i]*v[id[cur]][j],c[i][j]%=3; } return ret%k; } inline bool check(int cur) { for (ri i=1;i<cur;i++) { ri ret=0; for (ri j=1;j<=d;j++) ret+=v[id[cur]][j]*v[id[i]][j]; if (ret%k==0) { int x=id[i],y=id[cur]; if (x>y) swap(x,y); printf("%d %d\n",x,y); return 1; } } return 0; } int main(){ //scanf("%d %d %d",&n,&d,&k); n=read(); d=read(); k=read(); for (ri i=1;i<=n;i++) for (ri j=1;j<=d;j++) v[i][j]=read()%k; for (ri i=1;i<=n;i++) id[i]=i; int ks=5; while (ks--) { memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); random_shuffle(id+1,id+n+1); for (ri i=1;i<=n;i++) { int t=work(i); if (t!=(i-1)%2) if (check(i)) return 0; } } puts("-1 -1"); return 0; }