题意: 一个圆的边上平均放置了n个点, 这n个点连接了m条边, 问这个图形能不能顺时针旋转(0°,360°)得到相同的图形.
思路: 哈希的思想, 每个点都有自己的属性, 相隔x距离的另一个点给点a一条边就是a的一个属性, 把每个点的这些属性哈希成一个值, 所有点的值就会构成一个数组. 那么这个问题就变成能不能从2倍的这个数组中找到2次以上的它本身.(第一次是0°,第二次是360°,再有一次就是旋转x°得到对称图形.)
代码:
#include<bits/stdc++.h>
#define fuck(x) std::cout<<"["<<#x<<"->"<<x<<"]"<<endl;
using namespace std;
const int M=1e5+5;
const int inf=1e9+5;
const int mod=1e9+7;
vector<int>v[M];
int hx2[2*M];
int hx[M];
int nxt[M];
int n,m;
int KMP() {
memset(nxt,0,sizeof(nxt));
int la,lb;
la=n;
lb=m;
int j,k;
j=0;
k=-1;
nxt[0]=-1;
while(j<la) {
if(k==-1||hx[j]==hx[k]) {
j++;
k++;
nxt[j]=k;
} else {
k=nxt[k];
}
}
int ans=0;
j=0;
k=0;
while(k<lb) {
if(j==-1||hx[j]==hx2[k]) {
j++;
k++;
} else {
j=nxt[j];
}
if(j==la) {
j=nxt[j];
ans++;
}
}
return ans;
}
int main() {
int k;
scanf("%d%d",&n,&k);
m=2*n;
for(int i=0; i<k; i++) {
int a,b;
scanf("%d%d",&a,&b);
if(a>b)
swap(a,b);
v[a].push_back(min(b-a,n-b+a));//将一端为a点的边的长度放入a的vector
v[b].push_back(min(b-a,n-b+a));
}
for(int i=0; i<n; i++) {
//hx[i]为i点的属性,用n进制的一个数记录了vector中的数据
sort(v[i+1].begin(),v[i+1].end());
//这些数据是没有顺序的,只要两个点的vector集合相同,就是能够重合的点,所以要sort
for(int j=0; j<v[i+1].size(); j++) {
hx[i]=hx[i]*n+v[i+1][j];//自然溢出
}
}
for(int i=0; i<n; i++) {
hx2[i]=hx[i];
hx2[i+n]=hx[i];
}
if(KMP()<=2) {//如果能够在hx2中匹配到2次以上的hx1,那么就能够旋转.
printf("No\n");
} else {
printf("Yes\n");
}
return 0;
}