Timus 1383. Flower-garden Designs 题解

Timus 1383. Flower-garden Designs 题解

题意:

给你一些点集\(P_1,P_2,P_3...P_n\),你需要找到它们的等价类。

两个点集\(P_1,P_2\)是等价的,当且仅当:

  1. \(|P_1|=|P_2|\)
  2. 存在一个矩阵\(A\),满足\(\det (A)>0\)。将\(P_1\)中所有的向量乘上\(A\)得到\(P_2\)。(也就是说对\(P_1\)做平移,拉伸或旋转,不能翻折,能得到\(P_2\)
题解:

先考虑如何判定\(P_1,P_2\)是否等价。

\(P_1,P_2\)中的点为\(A_1,A_2,A_3...A_m\),\(B_1,B_2...B_m\)

首先除去一个特殊情况:\(P_1\)\(P_2\)中的点共线,这个非常好处理,直接记录相邻段的比值,然后除去\(gcd\),哈希判断就行了。

下面讨论不共线的情况:

由于只涉及到旋转和平移,所以很自然的可以想到找到点集的重心:\(O\),然后将所有点表示成与\(O\)的相对位置,\(A_i\leftarrow A_i-O_A\)\(B_i\leftarrow B_i-O_B\)

然后将它们移动到同一个直角坐标系中

由于不能翻转,所以我们可以先极角排序,这样向量之间的顺序就不会改变。

一种\(M^2\)的做法:

枚举对应关系\(A_iB_j,A_{i+1}B_{j+1}...\)(下标在模意义下)。

随便取出不共线的两组就可以解出矩阵\(A\)了。

优化:

考虑计算两两间的叉积:

\(A_i*A_j,B_i*B_j\)

可以发现其中一个必定是另一个的倍数,且倍数固定,这个倍数恰好就是\(\det(A)\),是一个定值。

如果我们记录两两间的叉积,然后判断是否循环同构,只是一个必要条件,只能说明相邻两个向量解出的矩阵的行列式相同。

但是判定相等必须拥有至少\(2n\)个值,所以可以记录\(A_i和A_{i+1},A_{i+2}\) 的叉积,有了\(2n\)个值,如果这\(2n\)个值循环同构的话,就可以说明算出的矩阵都是一样的了。

证明:

由于进行线性变换后两个向量的叉积会乘上\(\det(M)\)\(M\)是线性变换的矩阵:

\[\begin{pmatrix} x_1&y_1 \\ x_2&y_2 \end{pmatrix} \times M= \begin{pmatrix} x_1'&y_1‘ \\ x_2'& y_2' \end{pmatrix} \]

其中叉积是左边矩阵的\(\det\)

所以叉积结果必须成比例,这是个必要条件。

如果将一个叉积结果除去这个比例,可以理解成消除了变换的影响。

剩下的只需要比较叉积序列是否循环同构就可以了,由于有\(2n\)个变量(每个坐标\((x,y)\)看作两个变量),所以存储\(2n\)个叉积信息就足够了。

实现细节:
  1. 将共线的情况特判掉。
  2. 如果\(A_i,A_{i+1},A_{i+2}\)都共线,则需要将\(A_i,A_{i+2}\)的叉积替换成\(A_{i},A_j\)的叉积(\(j\)\(i\)后面第一个和\(A_{i},A_{i+1}\)不共线的点)
  3. 下标最好都\(\times n\),这样就可以全整数运算,避免精度错误。
  4. 如果刚好有一个点在重心上,需要额外记录一个信息。

code

typedef pair<int,int> vec;
#define x FIR
#define y SEC
int cross(vec A,vec B){
	return A.x*B.y-A.y*B.x;
}
int dot(vec A,vec B){
	return A.x*B.x+A.y*B.y;
}
vec operator - (vec A,vec B){
	return II(A.FIR-B.FIR,A.SEC-B.SEC);
}
vec operator + (vec A,vec B){
	return II(A.x+B.x,A.y+B.y);
}
const int MAXN=1e4+233; 
int n;
map<pair<int,int>,vector<int> > clss;
const int MOD=1e9+7;
const int MUL=232333;
int quick(int A,int B){
	int ret=1;
	while(B){
		if(B&1) ret=1ll*ret*A%MOD;
		B>>=1;
		A=1ll*A*A%MOD;
	}
	return ret;
}
int inv(int A){return quick(A,MOD-2);}
double ang(vec VV){
	return atan2(VV.y,VV.x);
}
bool cmp(vec A,vec B){
	if(abs(atan2(A.y,A.x)-atan2(B.y,B.x))<1e-8) return A.x*A.x+A.y*A.y<B.x*B.x+B.y*B.y;
	return atan2(A.y,A.x)<atan2(B.y,B.x);
}
int Hash(vector<int> v){
	int g=v.front();
	for(auto it:v){
		g=__gcd(g,it);	
	}
	g=abs(g);
	for(auto & it:v) it/=g;
	int Tmp=1;
	int ret=0;
	for(auto it:v){
		ret+=1ll*Tmp*it%MOD;
		ret%=MOD;
		Tmp=1ll*Tmp*MUL%MOD;
	}
	return ret;
}
int Small_Represent(vector<int> v){
	int g=*max_element(ALL(v));
	for(auto it:v){
		if(it) g=__gcd(g,it);
	}
	g=abs(g);
	for(auto & it:v) it/=g;
	int siz=v.size();
	int ret=MOD;
	rep(i,siz) v.PB(v[i]);
	int Tmp=0;
	int mull=1;
	rep(i,siz){
		(Tmp+=1ll*mull*v[i]%MOD)%=MOD;
		mull=1ll*mull*MUL%MOD;
	}
	mull=1ll*mull*inv(MUL)%MOD;
	ret=Tmp;
	rb(i,1,siz-1){
		Tmp-=v[i-1];
		Tmp+=MOD;
		Tmp%=MOD;
		Tmp=1ll*Tmp*inv(MUL)%MOD;
		Tmp+=1ll*mull*v[i+siz-1]%MOD;
		Tmp%=MOD; 
		check_min(ret,Tmp);
	}
	return ret;
}
void work(vector<vec> v,int id){
	if(v.size()==1){
		clss[II(2,0)].PB(id);
		return ;
	}
	sort(ALL(v));
	bool line=1;
	if(v.size()==2);
	else{
		rb(i,2,v.size()-1){
			if(cross((v[i]-v[0]),(v[i-1]-v[0]))) line=false;
		}
	}
	if(line){
		vector<int> Tmp1,Tmp2;
		if(v[0].x==v[1].x)
		rb(i,1,v.size()-1) Tmp1.PB(v[i].y-v[i-1].y);
		else
		rb(i,1,v.size()-1) Tmp1.PB(v[i].x-v[i-1].x);
		Tmp2=Tmp1;
		reverse(ALL(Tmp2));
		clss[II(1,min(Hash(Tmp1),Hash(Tmp2)))].PB(id);
		return ;
	}
	vector<int> V;
	vector<vec> newv;
	vec heavy=II(0,0);
	for(auto it:v){
		heavy=heavy+it;
	}
	for(auto & it:v){
		it.FIR*=v.size();
		it.SEC*=v.size();
		if(it!=heavy) newv.PB(it-heavy);
	}
	sort(ALL(newv),cmp);
	int siz=newv.size();
	rep(i,siz) newv.PB(newv[i]);
	int j=0;
	rep(i,siz){
		while(abs((ang(newv[j])-ang(newv[i])))<1e-8)++j;
		V.PB(cross(newv[i],newv[i+1]));
		V.PB(cross(newv[i],newv[j]));
	}
	clss[II(0,1ll*Small_Represent(V)*v.size()%MOD)].PB(id);
}
signed main(){
	scanf("%lld",&n);
	rb(i,1,n){
		int sz;
		vector<vec> v;
		scanf("%lld",&sz);
		rb(j,1,sz){
			vec tmp;
			scanf("%lld%lld",&tmp.x,&tmp.y);
			v.PB(tmp);
		}
		work(v,i);
	}
	cout<<clss.size()<<endl;
	for(auto it:clss){
		for(auto itt:it.SEC){
			printf("%lld ",itt);
		}
		puts("0");
	}
	return 0;
}
posted @ 2021-04-27 22:26  WWW~~~  阅读(61)  评论(0编辑  收藏  举报