HDU4917 Permutation(状态压缩dp,图论)

来源:互联网 发布:spss mac使用教程 编辑:程序博客网 时间:2024/06/15 20:06


Permutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 896    Accepted Submission(s): 281


Problem 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 11 23 21 22 3
 

Sample Output
31
 

Author
Xiaoxu Guo (ftiasch)
 

Source
2014 Multi-University Training Contest 5
 








题意:一个1-n的排列,给出其中一些数之间的约束条件.问排列的总个数

相当于给出一张图,求拓扑排序的总数是多少.

首先观察数据范围,一共的点不超过40个,一共的边不超过20个,意味着图不一定联通,而且一个连通块中的点最多21个.

我们首先把图分成一个个连通块,依次对每一个联通块处理,一个联通块带来的新的排序树是C(sum,tot)*连通块内拓补排序的总数,sum是剩下的未处理的点的总数.

对于一个连通块内部,用状压dp的方法求出拓扑排序总数.具体的方案是,对于每一个状态i,枚举放到整个拓扑序列的最后一个点,如果点j的前驱都属于i,那么dp[i]+=dp[i&*(~(1<<j))]




#include <map>#include <iostream>#include <algorithm>#include <cstring>#include <cstdio>using namespace std;const int MAXN=50;const long long mod=1e9+7;int d[MAXN][MAXN];int vis[MAXN];int pre[MAXN];int a[MAXN];long long C[MAXN][MAXN];int tot=0;int n,m;void dfs(int s){    int tmp=tot;    a[tot++]=s;    vis[s]=1;    for(int i=1;i<=n;i++){        if(d[s][i]){            if(!vis[i])                dfs(i);            if(d[s][i]==1){                for(int j=0;j<tot;j++){                    if(a[j]==i){                        pre[j]|=(1<<tmp);                    }                }            }        }    }}long long dp[1<<21];long long cal(){    memset(dp,0,sizeof(dp));    dp[0]=1;    for(int s=0;s<(1<<tot);s++){        for(int i=0;i<tot;i++){            if(((s&pre[i])==pre[i])&&!(s&(1<<i)))                dp[s|(1<<i)]=(dp[s|(1<<i)]+dp[s])%mod;        }    }    return dp[(1<<tot)-1];}int main(){    for(int i=0;i<41;i++){        C[i][0]=1;        for(int j=1;j<=i;j++)            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;    }    while(scanf("%d%d",&n,&m)!=EOF){        memset(d,0,sizeof(d));        memset(vis,0,sizeof(vis));        for(int i=0;i<m;i++){            int u,v;            scanf("%d%d",&u,&v);            d[u][v]=1;            d[v][u]=-1;        }        long long x=n;        long long ans=1;        for(int i=1;i<=n;i++){            if(!vis[i]){                tot=0;                memset(pre,0,sizeof(pre));                dfs(i);                if(tot<2)                    ans=(((tot*C[x][tot])%mod)*ans)%mod;                else                    ans=((cal()*C[x][tot])%mod*ans)%mod;                x-=tot;            }        }        printf("%lld\n",ans);    }}