51nod 1282 時鐘 (雜湊、字串的最小表示法)
阿新 • • 發佈:2018-12-09
題目
題解
要判斷時鐘是否相同,只需將時鐘的指標排序後求出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;
}