[uva11174]村民排队 递推+组合数+线性求逆元

n(n<=40000)个村民排成一列,每个人不能排在自己父亲的前面,有些人的父亲不一定在。问有多少种方案。

父子关系组成一个森林,加一个虚拟根rt,转化成一棵树。

假设f[i]表示以i为根的子树的排列方案数。

f[i]=f[1]*f[2]*..f[k] /(sum[i]-1)!/sum[1]!*sum[2]!*..sum[k]!)

化简,对每一个i,sum[i]-1在分子出现一次,sum[i]在分母出现一次。

Ans = n!/(sum1*sum2*sum3*...*sumn)

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6
 7 typedef long long LL;
 8 const int mod=((int)1e9)+7,maxn=40000,N=40010;
 9 int first[N],sum[N],fa[N];
10 LL jc[N],inv[N];
11 int rt,al;
12 struct node{int x,y,next;}a[N];
13
14 void ins(int x,int y)
15 {
16     a[++al].x=x;a[al].y=y;
17     a[al].next=first[x];first[x]=al;
18 }
19
20 void dfs(int x)
21 {
22     sum[x]++;
23     for(int i=first[x];i;i=a[i].next)
24     {
25         dfs(a[i].y);
26         sum[x]+=sum[a[i].y];
27     }
28 }
29
30 int main()
31 {
32     freopen("a.in","r",stdin);
33
34     jc[1]=1;
35     for(int i=2;i<=maxn;i++) jc[i]=(jc[i-1]*i)%mod;
36     inv[1]=1;
37     for(int i=2;i<=maxn;i++)
38     {
39         inv[i]=((LL)(mod-mod/i))*inv[mod%i]%mod;
40     }
41
42     int T,n,m,x,y;
43     scanf("%d",&T);
44     while(T--)
45     {
46         scanf("%d%d",&n,&m);
47         rt=n+1;
48         for(int i=1;i<=n;i++) fa[i]=-1;
49         al=0;
50         memset(first,0,sizeof(first));
51         for(int i=1;i<=m;i++)
52         {
53             scanf("%d%d",&x,&y);
54             fa[x]=y;
55             ins(y,x);
56         }
57         for(int i=1;i<=n;i++)
58             if(fa[i]==-1) fa[i]=rt,ins(rt,i);
59         memset(sum,0,sizeof(sum));
60         dfs(rt);
61         LL ans=jc[sum[rt]-1];
62         for(int i=1;i<=n;i++)
63         {
64             ans=ans*inv[sum[i]]%mod;
65         }
66         printf("%lld\n",ans);
67     }
68
69     return 0;
70 }

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>using namespace std;
typedef long long LL;const int mod=((int)1e9)+7,maxn=40000,N=40010;int first[N],sum[N],fa[N];LL jc[N],inv[N];int rt,al;struct node{int x,y,next;}a[N];
void ins(int x,int y){a[++al].x=x;a[al].y=y;a[al].next=first[x];first[x]=al;}
void dfs(int x){sum[x]++;for(int i=first[x];i;i=a[i].next){dfs(a[i].y);sum[x]+=sum[a[i].y];}}
int main(){freopen("a.in","r",stdin);jc[1]=1;for(int i=2;i<=maxn;i++) jc[i]=(jc[i-1]*i)%mod;inv[1]=1;for(int i=2;i<=maxn;i++){inv[i]=((LL)(mod-mod/i))*inv[mod%i]%mod;}int T,n,m,x,y;scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);rt=n+1;for(int i=1;i<=n;i++) fa[i]=-1;al=0;memset(first,0,sizeof(first));for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);fa[x]=y;ins(y,x);}for(int i=1;i<=n;i++)if(fa[i]==-1) fa[i]=rt,ins(rt,i);memset(sum,0,sizeof(sum));dfs(rt);LL ans=jc[sum[rt]-1];for(int i=1;i<=n;i++){ans=ans*inv[sum[i]]%mod;}printf("%lld\n",ans);}return 0;}

原文地址:https://www.cnblogs.com/KonjakJuruo/p/9688582.html

时间: 2024-11-09 02:37:28

[uva11174]村民排队 递推+组合数+线性求逆元的相关文章

线性求逆元

简介 逆元,简单的来说就是a?b≡1(modp),那么b就是a关于p的逆元. 正常的来说用扩展欧几里得来做.复杂度不是线性的. 但是如果所有的i≤p,有一个线性求逆元的方法. 正常的来说 方法 因为i≤p,所以考虑用i来表示p,并要求表示出来的所有数都能用p和i表示. 设p=ki+b,k=?pi?,l=pmodi 那么ki+b≡0(modp) 因为要求的是i?1,所以需要把i?1独立起来,所以我们等式两边同时乘以i?1b?1 那么式子就可以变成kb?1+i?1≡0 然后把可以求得i的逆元的数放到

2014多校8(1001)hdu4945(dp+组合数计数+求逆元)

2048 Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 566    Accepted Submission(s): 129 Problem Description Teacher Mai is addicted to game 2048. But finally he finds it's too hard to get 2048.

线性求逆元推导

本篇介绍线性求逆元的推导过程 ·对于一个质数\(P\),我们需要求出\(1-N\)在\(mod\ P\)意义下的逆元,如何使用线性的方法求其逆元呢? ·首先,我们设\(t=P/i,k=P%i\); ·对于\(i*t+k≡0 \pmod{P}\),我们可以做出如下推导: ·等式两边同时除以\(i*k\),我们可以得到新式子\(\frac{t}{k}+\frac{1}{i}≡0 \pmod{P}\); ·从而得到:\(\frac{P}{i}*inv[P\%i]+inv[i]≡0 \pmod{P}\)

【BZOJ3823】【East!模拟赛_Round5T1】定情信物 推公式+线性筛逆元(推公式法比出题人简)

题解1: 我们定义点为0维元素.线为1维元素.面为2维元素-- 既然一个低维超方体在对应新轴上平移得到高一维的超方体,比如二维超方体为一个面,然后沿新出现的z轴拓展,那么一个低维元素就会增加一维变成高一维的元素,比如点变成线.线变成面.面变成体-- 这样就有一个推式: 高维元素会由第一维的元素衍生.同维元素复制保留, 也就是一个超方体升维之后,高维超方体第i维元素的数量将是原超方体第i维的数量*2+(i-1)维的数量. 然后经过鬼畜的推导/撞大运的找规律,可以得到一个组合数公式, 最后加个线性筛

HDU 4869 (递推 组合数取模)

Problem Turn the pokers (HDU 4869) 题目大意 有m张牌,全为正面朝上.进行n次操作,每次可以将任意ai张反面,询问n次操作可能的状态数. 解题分析 记正面朝上为1,朝下为0. 若最后有x个1,则对答案的贡献为C(n,x).所以只需要知道最后可能的1的个数. 假设已经有a个1,某次操作可以将b张牌反面,可以发现操作之后可能的1的个数相差2. 记录每次操作后1的个数所在区间为[l ,r],即可能取到的个数为l , l+2 , l+4 , ...... , r . 每

luogu P3811线性求逆元

首先扩O:T了一个点(因为上界松),83分. #include <cstdio> using namespace std; int n, p; void exgcd(int a, int p, int &b, int &x){ if (p==0){ b=1, x=0; return; } exgcd(p, a%p, b, x); int tmp=b; b=x; x=tmp-a/p*x; return; } int main(){ scanf("%d%d",

[HDOJ6172] Array Challenge(线性递推,黑科技)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6172 题意:给一堆东西,就是求个线性递推式,求第n项%1e9+7 杜教板真牛逼啊,线性递推式用某特征值相关的论文板,打表前几项丢进去就出结果了. 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 #define rep(i,a,n) for (ll i=a;i<n;i++) 6 #def

HDU - 6172:Array Challenge (BM线性递推)

题意:给出,三个函数,h,b,a,然后T次询问,每次给出n,求sqrt(an); 思路:不会推,但是感觉a应该是线性的,这个时候我们就可以用BM线性递推,自己求出前几项,然后放到模板里,就可以求了. 数据范围在1e15,1000组都可以秒过. 那么主要的问题就是得确保是线性的,而且得求出前几项. #include<bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per

矩阵乘法递推的优化艺术

对于一个线性递推式,求它第项的值,通常的做法是先构造一个的矩阵,然后在时间内求出. 其实,由于这个矩阵的特殊性,可以将时间优化到.接下来我会以一个题目来讲解矩阵乘法递推的优化. 题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1229 题意:设,求的值.其中,和 . 前言:本题如果用普通的矩阵做法,很明显会TLE.那么我们要对这个特殊的矩阵进行时间上的优化. 分析:本题主要可用两种方法解决,分别是错位相减和矩阵乘法