UVA11174村民排队问题

来源:互联网 发布:求购淘宝店铺 编辑:程序博客网 时间:2024/04/28 15:30
题意:
     有n个人要排队,给你一些父子关系,要求儿子不能站在自己的父亲前面,问有多少种排队方式?


思路:
      白书上的题目,首先我们可以把关系建成树,这样我们就有可能得到一个森林(或者是一课树),然后我们再虚拟出来一个点0连接所有森林的根节点,这样是为了保证是一棵树,然后题目就变成了给你一棵树,不改变关系,问这个树有多少种方式,这个还是排列组合问题,对于每一个根节点,有这样的性质


root[i] = f[1]*f[2]*..f[k]   *  (s[i]-1)!/s[1]!*s[2]!*..s[k]! 
f[1]..f[k]表示的当前根节点连接的k个儿子为根节点的树的排列个数,s[i]表示的是以i为根节点的这棵树的所有节点个数,上面的式子可以理解成这样
f[1]*f[2]*..f[k] 所有儿子为根节点的排列个数
(s[i] - 1)! 表示的是以i为根的这棵树的所有节点数-1(不算跟所以-1)的排列方式
//s[1]!*s[2]!*..s[k]!  相当于全排列去掉重复部分,因为每一棵树已经*f[]了,不能在*了,不能再*那就可以理解成重复部分了,所以...,然后把每一个公式都化简,会发现分子剩1,分母剩s[i]了,(可以理解成分子的f[i]和分母的s[i]!约),这样最后剩下的是
((n+1)-1)/s1*s2*..sn;
最后的公式是
Ans = n!/(s1*s2*s3*s4...*sn) si是以i为根节点的子树的节点数
这样就可以了,然后这样会设计到一个问题,那就是大数相除取余的问题,我知道应该有至少两种方法解决这个问题,一个是逆元,另一个是a/b%c = a * pow(b ,c - 2) % c,这个不解释了,可以自己去网上找学习,哎!想起了亚洲赛那道排列组合题,当时在赛场上怎么也想不起来大数相除的转换了,sb了!








#include<stdio.h>
#include<string.h>


#define MOD 1000000007
#define N_node 40000 + 10
#define N_edge 40000 + 10


typedef struct
{
   int to ,next;
}STAR;


STAR E[N_node];
long long sum[N_node];
long long jc[N_node];
int list[N_node] ,tot;
int du[N_node];


void add(int a ,int b)
{
   E[++tot].to = b;
   E[tot].next = list[a];
   list[a] = tot;
}


int DFS(int now)
{
   int s = 1;
   for(int k = list[now] ;k ;k = E[k].next)
   {
      s += DFS(E[k].to);
   }
   sum[now] = s;
   return s;
}


long long Pow(long long a ,long long b)
{
   long long c = 1;
   while(b)
   {
      if(b & 1) c *= a;
      b >>= 1;
      a *= a;
      a = a % MOD;
      c = c % MOD;
   }
   return c;
}


void DB()
{
   long long now = 1;
   for(long long i = 1 ;i <= 40000 ;i ++)
   {
      now = now * i % MOD;
      jc[i] = now;
   }
}
   


int main ()
{
   int n ,m ,i ,a ,b ,t;
   DB();
   scanf("%d" ,&t);
   while(t--)
   {
      scanf("%d %d" ,&n ,&m);
      memset(list ,0 ,sizeof(list)) ,tot = 1;
      memset(du ,0 ,sizeof(du));
      for(i = 1 ;i <= m ;i ++)
      {
         scanf("%d %d" ,&a ,&b);
         add(b ,a);
         du[a] ++;
      }
      for(i = 1 ;i <= n ;i ++)
      if(!du[i]) add(0 ,i);
      memset(sum ,0 ,sizeof(sum));
      DFS(0);
      long long Ans = jc[n];
      for(i = 1 ;i <= n ;i ++)
      {
          Ans = Ans * Pow(sum[i] ,MOD - 2) % MOD;
      }
      printf("%lld\n" ,Ans);
   }
   return 0;
}
      
      
      
   
      

















0 0
原创粉丝点击