题目链接:http://poj.org/problem?id=3349
题目大意:雪花上有6个数形成环状,只有当从某个位置为起点,按顺时针或逆时针,能完全和另一个雪花相同时,判断为有两个相同的雪花。现有n片雪花,判断是否存在两片相同的雪花。(n<=10^5)
题目分析:
1.首先这个题目是个天坑,10^5很容易让人想到用map来实现Hash,方便又快捷,然而,本题对单纯的Hash很不友好,数据有意在卡你,基本过不了。
2.其次10^5看似可以O(nlogn),然而本题卡常数,map必不可能过。
3.然后你就想到用链表来做Hash,对Hash值相同的数据循环判断一次对吧?非常遗憾,看似10^5随便过,连logn都没了,然而卡了你的常数。而且要注意你数据的离散程度,否则链表的效率可能还不如map。
4.最坑的地方来了:一定要注意在做Hash函数的时候,千万不能做乘法运算,对于雪花上的数(x<=10^6),乘法实在太慢了。
5.然后一个很坑的地方是,这个题不能用最小表示法,因为没有必要,且对于这种卡常数的题很容易挂掉。
6.最后就是要注意一下Hash值相同的时候的判断了,由于每片雪花的数字个数是一样的,所以我们可以把其中一个雪花固定从0号位开始,枚举另一个雪花的起点就可以了。
正解代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>using namespace std;
typedef long long ll;
const ll maxn=1000010;
ll n,tot=0,mod=999983;
ll snow[maxn][10],head[maxn],next[maxn];
ll Hash(ll (&A)[10])
{
ll sum=1;for(ll i=0;i<6;i++)sum=(sum+A[i])%mod;return sum%mod;
}
bool thesame(ll (&A)[10],ll (&B)[10])
{
for(ll i=0;i<6;i++){
bool flag=false;for(ll j=0;j<6;j++){
ll pos1=j,pos2=(i+j)%6;if(A[pos1]!=B[pos2]){
flag=true;break;}}if(!flag)return true;}for(ll i=0;i<6;i++){
bool flag=false;for(ll j=0;j<6;j++){
ll pos1=j,pos2=(5-i-j+6)%6;if(A[pos1]!=B[pos2]){
flag=true;break;}}if(!flag)return true;}return false;
}
bool insert(ll (&A)[10])
{
ll weight=Hash(A);for(ll i=head[weight];i!=0;i=next[i])if(thesame(snow[i],A))return true;++tot;for(ll i=0;i<6;i++)snow[tot][i]=A[i];next[tot]=head[weight];head[weight]=tot;return false;
}
int main()
{
scanf("%lld",&n);for(ll i=1;i<=n;i++){
ll temp[10];for(ll j=0;j<6;j++)scanf("%lld",&temp[j]);if(insert(temp)){
printf("Twin snowflakes found.");return 0;}}printf("No two snowflakes are alike.");return 0;
}