HDU 5399 数学 Too Simple
题意:有m个1~n的映射,而且对于任意的 i 满足 f1(f2(...fm(i))) = i
其中有些映射是知道的,有些是不知道的,问一共有多少种置换的组合。
分析:
首先这些置换一定是1~n的一个置换(也就是1~n的一个排列)才行,因为如果某两个数映射到同一个数的话,那么这个数往后无论怎么映射,这两个数最终映射的结果还是一样的。
如果所有的f都给出来的话,那么只要判断一下就行。
如果有一个置换不知道的话,这个置换是可以通过前后的置换计算出来的,所以只有唯一解。
如果有两个置换不知道的话,第一个置换可以任意确定,有n!种情况,第二个置换根据第一个置换确定。
以此类推,有c个未知的置换的话,其中c-1个可以自由确定,而且互补影响,最后一个置换根据前面所有置换唯一确定,所以中的方案数是(n!)c-1
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 typedef long long LL; 7 8 const int maxn = 100 + 10; 9 const LL MOD = 1000000007; 10 11 int n, m; 12 bool vis[maxn]; 13 int a[maxn][maxn]; 14 15 inline LL mul_mod(LL a, LL b) { return a * b % MOD; } 16 17 LL pow_mod(LL a, int n) 18 { 19 LL ans = 1LL, base = a; 20 while(n) 21 { 22 if(n & 1) ans = mul_mod(ans, base); 23 base = mul_mod(base, base); 24 n >>= 1; 25 } 26 return ans; 27 } 28 29 LL fac[maxn]; 30 31 int main() 32 { 33 fac[0] = 1; 34 for(int i = 1; i < maxn; i++) fac[i] = fac[i - 1] * i % MOD; 35 36 while(scanf("%d%d", &n, &m) == 2 && n) 37 { 38 int cnt = 0; 39 bool ok = true; 40 for(int i = 1; i <= m; i++) 41 { 42 scanf("%d", &a[i][1]); 43 if(a[i][1] == -1) { cnt++; continue; } 44 for(int j = 2; j <= n; j++) scanf("%d", &a[i][j]); 45 memset(vis, false, sizeof(vis)); 46 for(int j = 1; j <= n; j++) 47 { 48 if(vis[a[i][j]]) { ok = false; break; } 49 vis[a[i][j]] = true; 50 } 51 } 52 if(!ok) { puts("0"); continue; } 53 54 if(!cnt) 55 { 56 bool ok = true; 57 for(int i = 1; i <= n; i++) 58 { 59 int t = i; 60 for(int j = m; j >= 1; j--) t = a[j][t]; 61 if(t != i) { ok = false; break; } 62 } 63 if(ok) puts("1"); else puts("0"); 64 } 65 else printf("%I64d\n", pow_mod(fac[n], cnt - 1)); 66 } 67 68 return 0; 69 }