题解 P1224 【[NOI2013]向量内积】
NOI2013 向量内积
题目链接
分析
对于前12个数据点我们可以直接暴力枚举。期望分数为60分.下面考虑数据点13~20.
首先,
,
首先考虑模数为2的情况.
假若
因而对于
然而问题在于数据量很大的情况下显然不可能使用
首先我们打乱矩阵
如果该行向量与前
我们令向量
在取模意义下,当
通过多次随机调整行向量的顺序,我们判定失败的概率就会大大减小.同时由于我们在计算时可以同时更新
对于模数为
我们发现,在取模意义下有
我们构造矩阵
(如此巧妙的判定我琢磨了好几个小时)
下面给出代码.其中每个变量的意义与题面及上方所述的变量一一对应.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=101000,maxd=111;
int n,d,k;
int A[maxn][maxd],u[maxd],S[maxd][maxd],id[maxn];
inline int read(){
int res=0;
char ch=getchar(),ch1=ch;
while(!isdigit(ch))ch1=ch,ch=getchar();
while(isdigit(ch))
res=(res<<3)+(res<<1)+ch-'0',ch=getchar();
return ch1=='-'?-res:res;
}
bool check(int x,int y){
int ans=0;
for(register int i=1;i<=d;i++)
ans+=A[x][i]*A[y][i];
return ans%k;
}
int workadd(int x){
int ans=0;
if(k==2)
for(register int i=1;i<=d;u[i]^=A[x][i],++i)
ans^=A[x][i]&u[i];//模2时可以用位运算代替乘法与加法
else
for(register int i=1;i<=d;++i)
for(register int j=1;j<=d;S[i][j]+=A[x][i]*A[x][j],++j)
ans+=A[x][i]*A[x][j]*S[i][j]%k;
return ans%k;
}
int main()
{
n=read();d=read();k=read();
for(register int i=1;i<=n;++i){
id[i]=i;
for(register int j=1;j<=d;++j)
A[i][j]=read()%k;
}
for(register int T=0;T<6;++T){
k==2?memset(u,0,sizeof(u)):memset(S,0,sizeof(S));
random_shuffle(id+1,id+1+n);//调整行向量顺序
for(register int i=1;i<=n;++i)
if(workadd(id[i])!=(i-1)%k)
for(register int j=1;j<i;++j)
if(!check(id[i],id[j])){
if(id[i]>id[j])swap(i,j);
printf("%d %d\n",id[i],id[j]);
return 0;
}
}
puts("-1 -1");
return 0;
}