2014多校5(1007)hdu4917(状态压缩dp+拓扑排序)

Permutation

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


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


题意:求有向无环图的拓扑排序数

思路:状态压缩DP,dp[s]表示集合s中已经拓扑过的点的方案数,则对于点i,如果i不在s中并且i的前驱全部都在s中,则可以转移dp[s|(1<<i)]+=dp[s],为什么是这样转移呢?

因为此时的i节点能够被加进去相当于i的入度已经减为0,则前面的方案数不会影响到新加入的i,直接累加即可。

总的来说为如下公式:

dp[s]=∑dp[s-i]   (i为s中入度为0的点)

题目中边的数目不会超过20,所以每个连通块中的点的数量不会超过21,可以用状态压缩

最后的结果等于各个连通块求得的方案数乘以对应的组合数,假如连通块i的节点数为k,剩下还未算的节点数为n(包含这k个节点),则对于当前连通块的组合数为C n k,累

加各个连通块的方案数乘以组合数即是答案

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
using namespace std;
#define mod (1000000000+7)

typedef __int64 ll;
int ha[45];
int hh[45];
int dp[2097160];
int ss[45];
int n,m;
int d;
int head[45];
int edge[50];
int next[50];
int du[45];
int vis[45];
int cn;
int head1[45];
int edge1[50<<1];
int next1[50<<1];
int d1;
int q[45];

void add(int u,int v)
{
    edge[d]=v;
    next[d]=head[u];
    head[u]=d++;
}

void add1(int u,int v)
{
    edge1[d1]=v;
    next1[d1]=head1[u];
    head1[u]=d1++;
}

void dfs(int u,int s)
{
    int i;
    for(i=head[u];i!=-1;i=next[i]){
            ss[edge[i]]|=s;
            dfs(edge[i],ss[edge[i]]|(1<
       
       
      
      
     
     
    
    
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值