HDU 4917 Permutation

HDU 4917 Permutation

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4917

Description

bobo has a permutation p1,p2,…,pn of 1,2,…,n.
Knowing m extra constraints of form pai<pbi, bobo wanna count the number of different permutations modulo (109+7).
It is guaranteed that there is at least one such permutation.

Input

The input consists of several tests. For each tests:
The first line contains n,m (1≤n≤40,0≤m≤20). Each of the following m lines contain 2 integers ai,bi(1≤ai,bi≤n).

Output

For each tests:
A single number denotes the number of permutations.

Sample Input

3 1
1 2
3 2
1 2
2 3

Sample Output

3
1

题意:

给你n个数。从1到n。再给你m个关系每个关系表示前一个数字小于后一个数字。求总共有多少种排列方式。

题解:

见代码注释。分块枚举,然后状态压缩。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int INF = 123456789;
const long long  MOD = 1e9+7;
long long combine[50][50];
long long dp[1<<21];
long long ans;
int num,res;
bool vis[50];
vector <int > matrix[50];
vector <int > pre[50];
vector <int > tp[50];
int connect[50],connect_add[50];

void Combine()
{
    for (int i = 0; i <= 45; i++) {
        for (int j = 0; j <= i; j++) {
            if (j == 0 || i == j)
                combine[i][j] = 1;
            else combine[i][j] = (combine[i-1][j] + combine[i-1][j-1])%MOD;
        }
    }
}


void dfs(int u) //num 是联通块所有元素的个数和,connect联通块中的数字种类,connect_add 存放这些联通块在 num 中的地址;
{
    vis[u] = 1;
    connect[num] = u;
    connect_add[u] = num++;
    int size = matrix[u].size();
    for (int i = 0; i < size; i++) {
        int v = matrix[u][i];
        if (!vis[v])
            dfs(v);
    }
}

void solve()
{
    for (int i = 0; i < num; i++) {                     //计算前驱;
        tp[i].clear();
        int u = connect[i];                             //枚举这个分块每个出现元素;
        int size = pre[u].size();                       //计算这个元素的前驱数量;
        for (int j = 0; j < size; j++)
            tp[i].push_back(connect_add[pre[u][j]]);    //将每个元素的前驱的位置压入记录;
    }
    int nn = (1 << num);                                //num个元素全部出现的状压的状态;
    for(int i = 0; i < nn; i++) dp[i] = 0;              //dp初始化;
    dp[0] = 1;
    for (int i = 0 ; i < nn; i++) {
        if (dp[i] == 0)
            continue;
        for (int j = 0; j < num; j++) {                 //枚举每个元素;
            if ((i & (1 << j)) == (1 << j))             //如果这个元素出现过了继续(剪枝);
                continue;
            bool flag = 0;
            int size = tp[j].size();                    //枚举这里前驱的地址元素;
            for (int k = 0; k < size; k++) {
                int v = tp[j][k];                       //前驱地址;
                if ((i & (1 << v)) != (1 << v)) {       //如果前驱没有出现证明不是拓扑排序;
                    flag = 1;
                    break;
                }
            }
            if (flag == 0)
                dp[i|(1<<j)] = (dp[i|(1<<j)] + dp[i])%MOD; //将前驱加入;
        }
    }
    ans = (((combine[res][num]*dp[nn-1])%MOD)*ans)%MOD;   //计算分块数目,即在res空白中放置num个数字的排列种类*num个数字的排列种数;
    res -= num;                                           //空白数目更新;
}

int main()
{
    Combine();
    int n,m;
    while (~scanf("%d %d",&n,&m)) {
        for (int i = 1; i <= n; i++) {
            pre[i].clear();
            matrix[i].clear();
        }
        memset(vis,0,sizeof(vis));
        ans = 1;
        res = n;
        int u,v;
        for (int i = 1; i <= m; i++) {
            scanf("%d %d",&u,&v);
            matrix[u].push_back(v);
            matrix[v].push_back(u);
            pre[v].push_back(u);
        }
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                num = 0;
                dfs(i);
                solve();
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
posted @ 2016-04-15 09:42  Thecoollight  阅读(284)  评论(0编辑  收藏  举报