uva1436 - Counting heaps 统计树的个数

本文探讨了如何计算一棵树的合法标记方案数量,即确保每个节点的标签都不大于其父节点标签的方法总数。通过递归计算每个子树的节点数,并采用质因数分解与快速幂运算来高效求解取模后的结果。

We are given a rooted tree of n vertices. The vertices are to be labeled with numbers 1, 2,..., n so that each label is unique and the heap condition holds, i.e. the label of any vertex is less than the label of its parent. How many such labellings exist? Since this number may be quite large, calculate only its remainder modulo m.

Input 

The input contains several tree descriptions. The first line contains the number of input trees t (t$ \le$250). Each tree description begins with a line containing the size of the tree n (1$ \le$n$ \le$500000) and an integer m (2$ \le$m$ \le$109). n - 1 lines follow, i-th of which contains p(i + 1), the number of the parent of the i + 1-th vertex (1$ \le$p(i + 1)$ \le$i). Vertex number 1 will be the root in each tree, so its parent will not be given. Total size of the input will not exceed 50MB.

Output 

For each tree output the number of its valid labellings modulo given m.


Explanation for sample:The 8 possible labellings from the last example test case are as follows:

\epsfbox{p4390.eps}

Sample Input 

4 
3 1000000
1 
1 
4 1000000
1 
1 
1 
5 1000000
1 
2 
3 
4 
5 1000000
1 
1 
3 
3

Sample Output 

2 
6 
1 
8

  给了树的形状,节点编号1-N,父节点小于子节点,这样的树有多少个。

  跟训练指南上面那个村民排队(不能排在父亲前面)是一样的。根是i,假设每个子树有f(ci)中排法,每个子树有s(ci)个节点,先把每个子树中的元素看成一样的,就有(s(i)-1)!/(s(c1)*s(c2)...*s(ck)),再乘上每种子树的排法,也就是f(i)=f(c1)*f(c2)..*f(ck)*(s(i)-1)!/(s(c1)*s(c2)...*s(ck))。再把子树的f展开,注意到所有非根节点u以(s(u)-1)!在分子出现一次,以s(u)!在分母出现一次,最后化简完答案就是f(root)=(s(root)-1)!/(s(1)*s(2)..s(n)),因为s(root)=n+1,所以f(root)=n!/(s(1)*s(2)..s(n)),一般类似的问题都可以这么做。

  那么只需要统计出每个子树的节点个数就行了,递归应该会爆栈,用队列比较好。

  还有一个难点是取余,解决的办法是把分子分母都换成质数的幂的形式,在分子上幂是正的,分母上是负的,由于最后答案肯定是正整数,所以每个因子的系数肯定不会小于0的。快速幂就OK。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdlib>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAXN 500010
#define MAXM 50010
#define MAXNODE 4*MAXN
#define MOD 1000000000
#define eps 1e-9
using namespace std;
int T,P,N,M,isprime[MAXN],prime[MAXM],coef[MAXM],cnt[MAXN],isleaf[MAXN],fa[MAXN];
queue<int> q;
void prime_table(){
    P=0;
    isprime[1]=0;
    for(int i=2;i<MAXN;i++) isprime[i]=1;
    for(int i=2;i<MAXN;i++){
        if(isprime[i]){
            isprime[i]=P;
            prime[P++]=i;
        }
        for(int j=i+i;j<MAXN;j+=i) isprime[j]=0;
    }
}
void init(){
    memset(isleaf,0,sizeof(isleaf));
    memset(fa,-1,sizeof(fa));
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++) cnt[i]=1;
    for(int i=2;i<=N;i++){
        scanf("%d",&fa[i]);
        isleaf[fa[i]]++;
    }
    while(!q.empty()) q.pop();
    for(int i=1;i<=N;i++) if(!isleaf[i]) q.push(i);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        if(u==1) continue;
        cnt[fa[u]]+=cnt[u];
        isleaf[fa[u]]--;
        if(!isleaf[fa[u]]) q.push(fa[u]);
    }
}
long long bigpow(int x,int n,int M){
    long long ret=1,t=x%M;
    while(n){
        if(n&1) ret=ret*t%M;
        t=t*t%M;
        n>>=1;
    }
    return ret;
}
void fac(int x,int v){
    for(int i=0;prime[i]<=x;i++){
        if(x==1) return;
        while(x%prime[i]==0){
            coef[i]+=v;
            x/=prime[i];
        }
        //不加下面这个直接超时
        if(isprime[x]){
            coef[isprime[x]]+=v;
            return;
        }
    }
}
long long solve(){
    memset(coef,0,sizeof(coef));
    for(int i=2;i<N;i++) fac(i,1);
    for(int i=2;i<=N;i++) fac(cnt[i],-1);
    long long ret=1;
    for(int i=0;i<P;i++) if(coef[i]) ret=ret*bigpow(prime[i],coef[i],M)%M;
    return ret;
}
int main(){
    freopen("in.txt", "r", stdin);
    prime_table();
    scanf("%d",&T);
    while(T--){
        init();
        printf("%lld\n",solve());
    }
    return 0;
}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值