【HDOJ 5399】Too Simple

【HDOJ 5399】Too Simple

函数映射问题 给出m函数 里面有0~m个函数未知(-1)
问要求最后1~n分别对应仍映射1~n 有几种函数写法(已给定的函数不可变 只可更改未知的函数的映射)

如果映射过程中出现多对一 即入度n出度小于n 的函数 必定冲突 即最后必有落单 映射失败 为0

如果映射完整 已知的连续函数可压缩成一个函数 中间出入度可忽略 因此可压缩为-1 f -1 -1 f -1 -1 f这种形态 进一步深入可发现中间的f仅仅起到通道作用 即可压缩为连续的-1之间的映射 已知的函数只起到”折射作用” 假设一段连续的函数对应为
1 2 3
3 2 1
只是进行了扭曲 消除后不影响方案数 即经过与否不重要

连续的-1构成的方案数为 n!^(x-1) x为-1的个数

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#define sc "%lld\n"
#define ll long long
#define mod 1000000007

using namespace std;

ll a[101] = { 1, 1};
int f[101][101];

ll pow(ll x,int cnt)
{
    int i;
    ll ans = 1;
    for(i = 0; i < cnt; ++i) ans = ans*x%mod;
    return ans;
}

int main()
{
    int n,m,i,j,cnt,x;
    bool isok;
    for(i = 2; i <= 100; ++i) a[i] = a[i-1]*i%mod;

    while(~scanf("%d %d",&n,&m))
    {
        cnt = 0;
        isok = 1;
        for(i = 1; i <= m; ++i)
        {
            scanf("%d",&f[i][1]);
            set <int> s;
            s.insert(f[i][1]);
            if(~f[i][1])
            {
                for(j = 2; j <= n; ++j)
                {
                    scanf("%d",&f[i][j]);
                    s.insert(f[i][j]);
                }
                if(s.size() < n) isok = 0;
            }
            else cnt++;
        }

        if(!isok)  puts("0");
        else if(cnt) printf(sc,pow(a[n],(cnt-1)));
        else
        {
            for(i = 1; i <= n; ++i)
            {
                x = i;
                for(j = m; j >= 1; --j)
                {
                    x = f[j][x];
                }
                if(x != i) break;
            }
            if(i != n+1) puts("0");
            else puts("1");
        }
    }
    return 0;
}

你可能感兴趣的:(组合)