把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷1224】[NOI2013] 向量内积(随机+构造)

点此看题面

  • 给定\(n\)\(d\)维向量,要求找出一对向量满足它们的内积是\(k\)的倍数。
  • \(n\le10^5,d\le30\)\(n\le2\times10^4,d\le100\)\(k=2\)\(3\)

前缀共同判定

\(k=2\)为例,如果前\(i-1\)个向量与\(i\)的内积模\(2\)都不为\(0\)(只能为\(1\)),那么内积之和模\(2\)应该与\(i-1\)同余。

内积的运算是满足分配律的,所以如果前\(i-1\)个向量的和向量与\(i\)的内积与\(i-1\)\(2\)不同余,则前\(i-1\)个向量中必然存在至少一个与\(i\)的内积模\(2\)\(0\),在确定一个向量的情况下直接去暴力找一趟即可。

当然上述做法存在缺陷,尽管模\(2\)同余也不一定不存在,因此我们要对给定的向量\(random\_shuffle\),以防良心出题人的“良心数据”。

\(k=3\)有些不太一样,因为模\(3\)余数不定,也就很难类似判定。

但发现\(1^2\equiv2^2\equiv1(mod\ 3)\),所以我们只要求出前\(i-1\)个向量与\(i\)的内积的平方和与\(i-1\)\(3\)是否同余即可。

平方直接大力拆开:\((\sum_{x=1}^da_{i,x}a_{j,x})^2=\sum_{x=1}^d\sum_{y=1}^da_{i,x}a_{i,y}a_{j,x}a_{j,y}\)。因此对每种\(a_{j,x}a_{j,y}\)做个前缀和即可。

代码:\(O(nd^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define M 100
using namespace std;
int n,d,k,id[N+5],a[N+5][M+5],s[M+5],ss[M+5][M+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int main()
{
	RI i,j,p,q,t;for(read(n,d,k),i=1;i<=n;++i) for(id[i]=i,j=1;j<=d;++j) read(a[i][j]),a[i][j]%=k;
	srand(71717171);W(random_shuffle(id+1,id+n+1),true)//random_shuffle
	{
		if(k==2) for(i=1;i<=n;++i)//如果k=2
		{
			for(t=0,p=1;p<=d;++p) t^=a[id[i]][p]&s[p],s[p]^=a[id[i]][p];//统计前i-1个向量与第i个向量内积和
			if(t==((i-1)&1)) continue;//如果同余不一定存在
			for(j=1;j^i;++j) {for(t=0,p=1;p<=d;++p) t^=a[id[i]][p]&a[id[j]][p];if(!t) break;}//暴力找
			return printf("%d %d\n",min(id[i],id[j]),max(id[i],id[j])),0;//注意尽管有SPJ,但题面要求必须满足p<q
		}
		else for(i=1;i<=n;++i)//如果k=3
		{
			for(t=0,p=1;p<=d;++p) for(q=1;q<=d;++q)
				t+=a[id[i]][p]*a[id[i]][q]*ss[p][q],(ss[p][q]+=a[id[i]][p]*a[id[i]][q])%=3;//统计前i-1个向量与第i个向量内积平方和
			if(t%3==(i-1)%3) continue;//如果同余不一定存在
			for(j=1;j^i;++j) {for(t=0,p=1;p<=d;++p) t+=a[id[i]][p]*a[id[j]][p];if(!(t%3)) break;}//暴力找
			return printf("%d %d\n",min(id[i],id[j]),max(id[i],id[j])),0;
		}
	}return 0;
}
posted @ 2021-03-31 14:34  TheLostWeak  阅读(87)  评论(0编辑  收藏  举报