JZOJ5273. 亲戚

来源:互联网 发布:桌面任务提醒软件 编辑:程序博客网 时间:2024/04/29 02:25

这里写图片描述

题解

很显然,这题是树形dp。
fi 表示i的子树的方案数。
考虑两个子树合并,
设其中一棵子树的大小是s1,方案数是f1,
另外一棵子树的大小是s2,方案数是f2,
那么它们合并起来的方案数就是
Cs1s1+s2f1f2
组合数的意义:合并后的序列总长度是s1+s2,在其中选出s1个作为第一个序列的,
那么剩下的就是第二个序列的。

code

#include<cstdio>#include<iostream>#include<algorithm>#include <cstring>#include <string.h>#include <cmath>#include <math.h>#define ll long long#define N 200003#define db double#define P putchar#define G getchar#define mo 1000000007using namespace std;char ch;void read(int &n){    n=0;    ch=G();    while((ch<'0' || ch>'9') && ch!='-')ch=G();    int w=1;    if(ch=='-')w=-1,ch=G();    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=G();    n*=w;}int a[N],next[N],b[N],size[N];ll f[N],jc[N*2],ny[N*2];int n,x,tot;ll ksm(ll x,int y){    ll s=1;    while(y)    {        if(y%2)s=s*x%mo;        x=x*x%mo;        y=y>>1;    }    return s;}void ins(int x,int y){    next[++tot]=b[x];    a[tot]=y;    b[x]=tot;}ll C(int x,int y){    return jc[y]*ny[x]%mo*ny[y-x]%mo;}void dg(int x){    f[x]=1;    for(int i=b[x];i;i=next[i])    {        dg(a[i]);        f[x]=f[a[i]]*f[x]%mo*C(size[a[i]],size[x]+size[a[i]])%mo;        size[x]+=size[a[i]];    }    size[x]++;}int main(){    ny[0]=jc[0]=1;    read(n);    for(int i=1;i<=n;i++)        read(x),ins(x,i),jc[i]=jc[i-1]*i%mo,ny[i]=ksm(jc[i],mo-2);    for(int i=n+1;i<=2*n;i++)        jc[i]=jc[i-1]*i%mo,ny[i]=ksm(jc[i],mo-2);    dg(0);    printf("%lld\n",f[0]);}