【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;
}

 

转载于:https://www.cnblogs.com/shxnb666/p/11426210.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值