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

62 篇文章 2 订阅
22 篇文章 0 订阅


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 p 1,p 2,…,p n of 1,2,…,n.

Knowing m extra constraints of form p ai<p bi, bobo wanna count the number of different permutations modulo (10 9+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 a i,b i(1≤a i,b i≤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
 

Author
Xiaoxu Guo (ftiasch)
 

Source
 








题意:一个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);
    }
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值