题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5399
解题思路:
题目大意:
给m个函数,其对应是自变量x属于{1,2,...n},f(x)属于{1,2...n},给出其中一些函数,问有多少种不同的函数集合,使得1<=i<=n,f1(f2(f3...fm(i))) = i。
解释样例:
3 3
1 2 3
-1
3 2 1
样例写成函数的形式就是
n=3,m=3(m行,每行n个数)
f1(1)=1,f1(2)=2,f1(3)=3
f2(1)、f2(2)、f2(3)的值均未知
f3(1)=3,f3(2)=2,f3(3)=1
所以要使所有的i(1≤i≤n),满足f1(f2(⋯fm(i)))=i,只有一种组合情况,即f2(1)=3,f2(2)=2,f2(3)=1
官方题解:
首先要求每个fi是个排列,否则如果某个fi将两个数映射向同一个数,那么最后这两个数得到的值一定相同。
如果还剩一个位置为−1,那么这个排列是唯一确定的,假设X∗fi∗Y=I,那么fi=X−1∗Y−1.
所以假设有c(c≥1)个−1,那么答案为(n!)c−1个可行的方案。
算法思想:
当某一行的映射不能保证一对一时(换句话说就是多对一),那么一定存在i(1≤i≤n)不满足f1(f2(⋯fm(i)))=i,此时结果为0。除了这种特殊情况,最后的解只跟-1的个数有关,当-1的个数是0时,结果未必是0,,要判断对所有的i是否都满足条件,如果满足,结果是1,否则结果是0。当只有一个-1的时候,因为对应关系都已经确定了,所以只有1种可行解,而当有两个-1时,其中一个函数的值可以根据另一个函数的改变而确定下来,故有n!种解。依此类推,当有k个-1时,解为(n!)^(k-1)(k>0)。
AC代码:
#include <iostream>
#include <cstdio>
#define MOD 1000000007
using namespace std;
typedef long long ll;
int n,m;
ll f[105],a[105][105];
int main(){
f[0] = 1;
for(int i = 1; i <= 100; i++)
f[i] = f[i-1]*i%MOD;
while(~scanf("%d%d",&n,&m)){
ll tot = 0,ans = 1;
for(int i = 1; i <= m; i++){
scanf("%lld",&a[i][1]);
if(a[i][1] == -1)
tot++;
else{
for(int j = 2; j <= n; j++){
scanf("%lld",&a[i][j]);
for(int k = j-1; k >= 1; k--)
if(a[i][j] == a[i][k])
ans = 0;
}
}
}
for(int i = 1; i < tot; i++)
ans = ans*f[n]%MOD;
if(tot == 0){
for(int i = 1; i <= n; i++)
a[0][i] = i;
for(int i = m; i>= 1; i--)
for(int j = 1; j <= n; j++)
a[0][j] = a[i][a[0][j]];//映射转换
for(int i = 1; i <= n; i++)
if(a[0][i] != i)
ans = 0;//如果不能变回原来的数列,则为0
}
printf("%lld\n",ans);
}
return 0;
}