UVa 11174 Stand in a Line

依旧是《训练指南》上的一道例题。书上讲的比较抽象,下面就把解法具体一下。因为涉及到父子关系,因此自然而然可以将n个节点构造成一棵树,最后将形成一个森林。接下来将使用递归的手法。设f(i)是以节点i为树根的子树,节点i有儿子c1,c2,c3....cj共j棵子树。s[i]为树根为i的子树包含的节点数。如果分别先给各个子树内部排序,那么毫无疑问,

共有f(c1)*f(c2)*f(c3)....*f(cj)种情况。接下来又要将所有子树的节点排成一个序列。那么我们事先不考虑各个子树的内部排序,共有(s[i]-1)!/(s[c1]!*s[c2]!*.....s[cj]!种情况。最后,我们将两种情况合起来,就是f(i)的解了,为f(c1)*f(c2)....*f(cj)*(s[i]-1)!/(s[c1]!*s[c2]!*.....s[cj]!)中情况。

事实上,接下来还可以进一步化简,设c1有孩子节点x1,x2,x3....xk。那么f(c1)=f(x1)*f(x2).....f(xk)*(s[c1]-1)!/(s[x1]!*x[x2]!....*x[xk]!)。将f(c1)代入,分子中的(s[c1]-1)!和分母中的s[c1]!约分的s[c1],然后依次类推,最后分母必然会化为s[1]*s[2]*s[3]...s[n]。因为总共n个节点构成一个森林,因此s[root](root可能为虚拟树根)为n+1,那么最后分母为n!。最后,求出n!/(s[1]*s[2]...s[n])就是我们要求的解。

还要注意的是,最后模运算的时候,1的逆为1。

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define MAX 40000+5
#define MOD 1000000007
#define LL long long
using namespace std;

LL fac[MAX],N[MAX];//fac[i]为i!,N[i]为i关于MOD的逆
int T,n,m;
int s[MAX],vis[MAX];
vector<int> Tree[MAX];

LL inv(LL a,LL n);
void gcd(LL a,LL b,LL& d,LL& x,LL& y);
int dfs(int);

int main()
{
    freopen("data.txt","r",stdin);
    fac[0]=fac[1]=N[1]=1;//注意1的逆为1
    for(LL i=2;i<MAX;++i){
        fac[i]=(i*fac[i-1])%MOD;
        N[i]=inv(i,MOD);
    }

    cin>>T;
    while(T--){
        cin>>n>>m;

        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;++i) Tree[i].clear();

        for(int i=0;i<m;++i){
            int a,b;
            cin>>a>>b;
            Tree[b].push_back(a);
        }

        for(int i=1;i<=n;++i){//DFS统计每棵子树包含的节点数
            if(!vis[i]) dfs(i);
        }

        LL ans=fac[n];
        for(int i=1;i<=n;++i){
            ans=(ans*N[s[i]])%MOD;
        }
        cout<<ans<<endl;
    }

    return 0;
}

LL inv(LL a,LL n)//求逆运算,参考《训练指南》
{
    LL d,x,y;
    gcd(a,n,d,x,y);
    return d==1?(x+n)%n:-1;
}

void gcd(LL a,LL b,LL& d,LL& x,LL& y)//扩展欧几里得算法
{
    if(!b){d=a;x=1;y=0;}
    else{gcd(b,a%b,d,y,x);y-=x*(a/b);}
}

int dfs(int r)
{
    int num=0;
    for(int i=0;i<Tree[r].size();++i){
        num+=dfs(Tree[r][i]);
    }

    vis[r]=1;
    s[r]=num+1;
    return s[r];
}
时间: 2024-07-29 16:54:00

UVa 11174 Stand in a Line的相关文章

uva 11174 - Stand in a Line(逆元+递推)

题目连接:uva 11174 - Stand in a Line 题目大意:村子里有n个村名民,现在他们要排成一列,处于对长辈的尊敬,他们不能排在自己父亲的前面,有些人的父亲不一定在村子了.问有多少种列的顺序. 解题思路:[算法竞赛入门经典-训练指南]的例题,主要还用到了欧几里得拓展定理求逆元. #include <cstdio> #include <cstring> #include <vector> using namespace std; typedef long

【递推】【推导】【乘法逆元】UVA - 11174 - Stand in a Line

http://blog.csdn.net/u011915301/article/details/43883039 依旧是<训练指南>上的一道例题.书上讲的比较抽象,下面就把解法具体一下.因为涉及到父子关系,因此自然而然可以将n个节点构造成一棵树,最后将形成一个森林.接下来将使用递归的手法.设f(i)是以节点i为树根的子树,节点i有儿子c1,c2,c3....cj共j棵子树.s[i]为树根为i的子树包含的节点数.如果分别先给各个子树内部排序,那么毫无疑问, 共有f(c1)*f(c2)*f(c3)

UVA 11174 Stand in a Line 树形dp+计数

题目链接:点击打开链接 题意:白书的P103. 加个虚根就可以了...然后就是一个多重集排列. import java.io.PrintWriter; import java.util.ArrayList; import java.util.Scanner; public class Main { static int N = 40100; ArrayList<Integer>[] G = new ArrayList[N]; static long mod = 1000000007; long

uva 12657 - Boxes in a Line(AC和TLE的区别,为什么说STL慢..)

用STL中的list写的,TLE #include<cstdio> #include<iostream> #include<cstring> #include<list> #include<algorithm> using namespace std; list<int> l; list<int>::iterator it1,it2,it3,it4,it5,it; void work(int a,int a1=1,int

UVa 12657 Boxes in a Line(双向链表的应用)

Boxes in a Line You have n boxes in a line on the table numbered 1 . . . n from left to right. Your task is to simulate 4 kinds of commands: ? 1 X Y : move box X to the left to Y (ignore this if X is already the left of Y ) ? 2 X Y : move box X to th

UVa 11174 (乘法逆元) Stand in a Line

题意: 有n个人排队,要求每个人不能排在自己父亲的前面(如果有的话),求所有的排队方案数模1e9+7的值. 分析: <训练指南>上分析得挺清楚的,把公式贴一下吧: 设f(i)为以i为根节点的子树的排列方法,s(i)表示以i为根的子树的节点总数. f(i) = f(c1)f(c2)...f(ck)×(s(i)-1)!/(s(c1)!s(c2)!...s(ck)!) 按照书上最开始举的例子,其实这个式子也不难理解,就是先给这些子树确定一下位置,即有重元素的全排列. 子树的位置确定好以后,然后再确定

UVa 12657 Boxes in a Line(应用双链表)

Boxes in a Line You have n boxes in a line on the table numbered 1 . . . n from left to right. Your task is to simulate 4 kinds of commands: ? 1 X Y : move box X to the left to Y (ignore this if X is already the left of Y ) ? 2 X Y : move box X to th

UVA 12657 Boxes in a Line

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4395 题目意思是说,给出一个数n,表示存在一个整数序列1--n,然后进行四种操作: 操作一:输入x,y,表示将x移到y的左边(若x本来就在y的左边则忽略): 操作二:输入x,y,表示将x移到y的右边(若x本来就在y的右边则忽略): 操作三:输入x,y,表示交换x和y. 操作四:将

Uva 12657 Boxes in a Line 双向链表

操作4比较特殊,为了避免一次性修改所有元素的指针,由于题目只要求输出奇数盒子的编号,所以我们可以灵活的根据是否进行过操作4对操作1 操作2 进行改动 操作3不受操作4影响 上代码.... #include<cstdio> #include<algorithm> const int maxn=100000+5; int right[maxn],left[maxn]; void link (int L,int R){ right[L]=R;left[R]=L; } //在双向链表这样复