51nod 1282 时钟 (哈希、字符串的最小表示法)

题目

这里写图片描述

题解

要判断时钟是否相同,只需将时钟的指针排序后求出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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值