题目
题解
要判断时钟是否相同,只需将时钟的指针排序后求出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;
}