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

点此看题面

  • 给定(n)(d)维向量,要求找出一对向量满足它们的内积是(k)的倍数。
  • (nle10^5,dle30)(nle2 imes10^4,dle100)(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^2equiv2^2equiv1(mod 3)),所以我们只要求出前(i-1)个向量与(i)的内积的平方和与(i-1)(3)是否同余即可。

平方直接大力拆开:((sum_{x=1}^da_{i,x}a_{j,x})^2=sum_{x=1}^dsum_{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
",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
",min(id[i],id[j]),max(id[i],id[j])),0;
		}
	}return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu1224.html