题意:
给定一个圆环,环上有 n n n 个整点,再给 m m m 条线段,每条线段端点是环上的整点,问能否通过旋转得到原图形。 ( n ≤ 1 0 5 , m ≤ 2 × 1 0 5 ) (n \leq 10^5, m \leq 2 × 10^5) (n≤105,m≤2×105)
链接:
https://codeforces.com/contest/1161/problem/B
解题思路:
对环上每个整点进行哈希,具体为:找到连接改点的所有线段,依照逆时针方向对另一端点排序,以逆时针方向在环上的距离作为线段距离,然后将线段情况哈希到该整点上,得到一个长度为 n n n 的数组,问题转化为该数组是否能循环右移得到原数组。在数组复制一遍添加到原数组末尾,利用 k m p kmp kmp 的 n x t nxt nxt 数组可以得到匹配情况。总复杂度 O ( m l o g m + n ) O(mlogm + n) O(mlogm+n)。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
typedef unsigned long long ull;
const ull base = 100313;
vector<int> G[maxn];
ull has[maxn];
int nxt[maxn];
int n, m, n2;
void cNext(){
int i = 0, j = -1; nxt[0] = -1;
while(i < n2){
if(j == -1 || has[i] == has[j]) nxt[++i] = ++j;
else j = nxt[j];
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; ++i){
int l, r; cin >> l >> r;
--l, --r;
G[l].pb((r - l + n) % n), G[r].pb((l - r + n) % n);
}
for(int i = 0; i < n; ++i){
has[i] = 0;
sort(G[i].begin(), G[i].end());
for(auto &v : G[i]){
has[i] = has[i] * base + v;
}
}
for(int i = 0; i < n - 1; ++i){
has[i + n] = has[i];
}
n2 = n * 2 - 1;
cNext();
int ret = 0;
for(int i = n; i <= n2; ++i){
if(nxt[i] >= n) { ret = 1; break; }
}
cout << (ret ? "Yes" : "No") << endl;
return 0;
}