D. Chladni Figure 传送门
题意:
给你一个圆,圆上有n个点,编号为1,2,…,n;圆上有m条边,问你旋转 k ∈ [ 1 , n − 1 ] k\in[1,n-1] k∈[1,n−1] 个单位,能不能与圆图形重合
解法
很容易想到 n*m的做法(枚举k),但是显然是会超时的,然后我看题解看不懂,再然后我就去问我的队友- - -无所不能的tls,果然,他一句话点醒了我。
题解上面说:旋转的k一定是n的因子,为什么呢?
举个例子:当n=4时,考虑 k=2和k=3两种情况,设a[i]代表点 i 的状态(上面连着的边)
-
当k=2满足条件时,则a[1] = a[3], a[2] =a[4] ,画一个图片可以这样理解
点 1 和点 2组成一个圈,(1,2)能够变为 (3,4), (3,4)恰好也能到达(1,2)
-
当k=3满足条件时,则a[1]=a[4]=a[3]=a[2] ??? 这不是k=1该有的情况吗???显然k=3是不满足的
按画图的话,就是(1,2,3)一团,剩下一个当个的点(4),很明显,会有问题。
按照上面所说,当k是n的因子的时候,才能恰好旋转 k 的单位。
因此枚举n的因子就行了,时间复杂度 O(n的因子数量*m)
很明显,n的因子不多,上述方法可行。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int main() {
int n,m;
cin>>n>>m;
vector<vector<int> >v(n+1);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
if(a>b)swap(a,b);
if(b-a==n-b+a) //特别注意,因为这样首可以与尾重叠,因此首位都要加入,可以由第四组样例看出来
{
v[a].push_back(b-a);
v[b].push_back(b-a);
}
else if(b-a<n-b+a) //只在顺时针方向第一个点压入长度
v[a].push_back(b-a);
else
v[b].push_back(n-b+a);
}
for(int i=1;i<=n;i++)
sort(v[i].begin(),v[i].end());
for(int k=1;k<n;k++)
{
if(n%k)continue;
bool ok=true;
for(int i=1;i<=n;i++)
if(v[i]!=v[(i+k>n)?(i+k-n):(i+k)]) //我也觉得很神奇,vector重载了==,差不多是用memcmp比较的
ok=false;
if(ok)
return printf("Yes\n"),0;
}
printf("No\n");
}