1282 时钟(最小表示法+hash)
有N个时钟,每个时钟有M个指针,P个刻度。时钟是圆形的,P个刻度均分整个圆。每个时钟每个指针指向整数刻度,并且每个时钟自身指针指向的数字都不同。你可以任意旋转时钟的表盘,但是你不能转指针。问最后有多少对时钟可以变成相同的状态。
例如:N = 5,M = 2,P = 4,5个时钟的数据如下{1, 2} {2, 4} {4, 3} {2, 3} {1, 3}
经过旋转后。 其中(1, 3), (1, 4), (2, 5) 和 (3, 4)是相同的。
给出所有时钟的数据,求有多少对时钟是相同的。
Input
第1行:3个数N, M, P中间用空格分隔,其中N为时钟的数量,M为表针的数量,P为刻度的数量(1 <= M, N <= 500, 1 <= P <= 10^9, M <= P)。
第2 - N + 1行:每行M个数,对应一个时钟,M个指针的位置。
Output
输出有多少对时钟是相同的。
Input示例
5 2 4
1 2
2 4
4 3
2 3
1 3
Output示例
4
//思路是有的,但是最小表示法不知道,一只wa。。。学习了最小表示法,还是很好理解的
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define LL long long 5 #define MX 505 6 7 const int hash_ = 133331; 8 9 int n, m, p; 10 int ans; 11 int ke[MX]; 12 int cha[MX]; 13 map<LL,int> zz; 14 15 int get_min(int *s, int len) //返回最小表示法的始端 16 { 17 int i = 0, j = 1, k = 0; 18 while(i<len && j<len && k<len) 19 { 20 int t = s[(i+k)%len]-s[(j+k)%len]; 21 if (!t) k++; 22 else 23 { 24 if (t>0) i += k+1; 25 else j += k+1; 26 if (i==j) j++; 27 k = 0; 28 } 29 } 30 return i<j?i:j; 31 } 32 33 void calc() 34 { 35 sort(ke,ke+m); 36 for (int i=1;i<m;i++) 37 cha[i-1] = ke[i]-ke[i-1]; 38 cha[m-1] = ke[0]+p-ke[m-1]; 39 int dex= get_min(cha,m); 40 41 LL has=0, quan =1; 42 for (int i=0;i<m;i++) 43 { 44 has += cha[(dex+i)%m]*quan; 45 quan*= hash_; 46 } 47 ans+=zz[has]; 48 zz[has]++; 49 } 50 51 int main() 52 { 53 scanf("%d%d%d",&n,&m,&p); 54 ans = 0; 55 for (int i=0;i<n;i++) 56 { 57 for (int j=0;j<m;j++) 58 scanf("%d",&ke[j]); 59 calc(); 60 } 61 printf("%d\n",ans); 62 return 0; 63 }