题意:
一个有n个数字的集合,有m次映射(函数),-1代表我们不知道的映射,可以随便安排,问经过你的安排之后,有多少种会使得最后的所有的f[i]=i。
思路:
首先,我们要知道,如果-1有很多个,假如是nn个,那么,前nn-1个-1我们都可以随便安排,因为最后一个-1一定可以把我们的映射变的合法,所以前nn-1个-1每个有n!种安排,所以有(n!)^(nn-1)种安排,而且如果m行中,某一行有重复的数字,那么一定输出0。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int f[110][110];
const int MOD = 1000000007;
int cal[110];
void prepare() {
long long now = 1;
cal[1] = 1;
for (int i = 2; i < 105; i++) {
now *= i;
now %= MOD;
cal[i] = (int)now;
}
}
long long pow(long long a, long long i, long long n) {
if (i == 0) {
return 1 % n;
}
int temp = pow(a, i>>1, n)%n;
temp = temp%n*temp%n;
if (i & 1) {
temp = (long long)temp * a % n;
temp %= n;
}
return temp % n;
}
int check(int num[], int cnt) {
int vis[200];
memset(vis, 0, sizeof(vis));
for (int i = 0; i < cnt; i++) {
vis[num[i]] = 1;
}
for (int i = 1; i <= cnt; i++) {
if (vis[i] == 0) {
return 0;
}
}
return 1;
}
int main()
{
int n, m;
prepare();
while (scanf("%d%d", &n, &m) != EOF) {
int tot = 0;
int tok = 0;
for (int i = 0; i < m; i++) {
int fir;
scanf("%d", &fir);
if (fir == -1) {
tot++;
f[i][0] = -1;
}
else {
f[i][0] = fir;
for (int j = 1; j < n; j++) {
scanf("%d", &f[i][j]);
}
if (check(f[i], n) == 0) {
tok = 1;
}
}
}
if (tok == 1) {
puts("0");
}
else if (tot == 1) {
puts("1");
}
else if (tot > 0) {
long long ans = (long long)pow(cal[n] % MOD, tot-1, MOD);
ans %= MOD;
printf("%d\n", (int)ans);
}
else {
int ok = 0;
for (int i = 0; i < n; i++) {
int now = f[m-1][i] - 1;
for (int j = m - 2; j >= 0; j--) {
now = f[j][now] - 1;
}
if (now != i) {
ok = 1;
break;
}
}
if (ok) {
puts("0");
}
else {
puts("1");
}
}
}
return 0;
}