题目
题解
要判断时钟是否相同,只需将时钟的指针排序后求出M个距离,然后看距离数组是否是循环同构即可。
循环同构:
abcd的循环同构有:abcd、bcda、cdba、dabc。
要判断是否循环同构,可以求出距离数组的最小表示。然后对这个最小表示数组求一个哈希值,判断这个哈希值是否相同。
最小表示就是所有循环同构中字典序最小的。
哈希的话,我用的是以前用过的一个方法:将每个值离散化为一个质数,然后累成,用unsigned long long自动取模2^64.
当然这个哈希方法不太好,时间复杂度比较高。
可以用hash=hash*133331+a[i],走一个循环。
AC代码
#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn=3e5+7;
vector<int> V;
int getid(int x)
{
return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
}
int prime[maxn]={2,3,5,7,11};
void init()
{
int i=5,n=13;
while(i<maxn)
{
int tmp=(int)sqrt(n+0.5),j;
for(j=0;prime[j]<=tmp;j++)
if(n%prime[j]==0) break;
if(prime[j]>tmp) prime[i++]=n;
n+=2;
}
}
int a[505][505];
map<ull,ll> mp;
set<ll> st;
set<ll>::iterator ite;
int s[505];
int minShow(int L)
{
int i=0,j=1,k=0;
while(i<L && j<L && k<L)
{
int t=s[i+k<L?i+k:i+k-L]-s[j+k<L?j+k:j+k-L];
if(!t) k++;
else
{
if(t>0) i+=k+1;
else j+=k+1;
if(i==j) j++;
k=0;
}
}
return min(i,j);
}
int ans[505];
int main()
{
int n,m,p;
init();
while(~scanf("%d%d%d",&n,&m,&p))
{
V.clear();
st.clear();
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
int x;
scanf("%d",&x);
a[i][j]=x;
}
sort(a[i],a[i]+m);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(j==0) continue;
V.push_back((a[i][j]-a[i][j-1]+p)%p);
}
V.push_back((a[i][m-1]-a[i][0]+p)%p);
}
V.erase(unique(V.begin(),V.end()),V.end());
mp.clear();
for(int i=0;i<n;i++)
{
for(int j=0;j<m-1;j++)
{
s[j]=(a[i][j+1]-a[i][j]+p)%p;
}
s[m-1]=(a[i][0]-a[i][m-1]+p)%p;
int pos=minShow(m);
ull x=1;
for(int j=0;j<m;j++)
{
ans[j]=s[j+pos<m?j+pos:j+pos-m];
int id=getid(ans[j]);
x*=(ull)prime[id];
}
mp[x]++;
st.insert(x);
}
ll ans=0;
for(ite=st.begin();ite!=st.end();ite++)
{
ll tmp=mp[(*ite)];
ans+=tmp*(tmp-1)/2;
}
printf("%lld\n",ans);
}
return 0;
}