bzoj5305 [Haoi2018]苹果树(组合数学)

首先n个点的这样生成的二叉树有n!种。
因此这就不再是一个期望问题,而是计数问题!
可能没逆元,烦躁qaq
考虑给每个点按生成顺序标号,我们来计算i->fa[i]这条边对答案的贡献。
我们枚举i的子树大小j,那么这条边就会被经过j*(n-j)次。
再考虑i子树内部形态数和外部形态数。
i子树内部形态数显然是 j!Cj1ni j ! ∗ C n − i j − 1 。(标号一定要大于i)
然后考虑把i子树看做一个点,1~i的点的二叉树形态有i!种。
然后把剩余的n-j-i+1个点挂到这棵树上,由于不能再向i子树内插点,所以形态数为 k=1nji+1(i+k2) ∏ k = 1 n − j − i + 1 ( i + k − 2 )
把这些都乘起来,化简一下,就得到了我们的答案:

i=1nj=1ni+1j!(nj)!Cj1nii(i1)j ∑ i = 1 n ∑ j = 1 n − i + 1 j ! ∗ ( n − j ) ! ∗ C n − i j − 1 ∗ i ∗ ( i − 1 ) ∗ j

没有除法哦!不过要 O(n2) O ( n 2 )

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 2010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,mod,C[N][N],fac[N],ans=0;
inline void inc(int &x,int y){x+=y;x%=mod;}
int main(){
//  freopen("a.in","r",stdin);
    n=read();mod=read();fac[0]=1;
    for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod;
    for(int i=0;i<=n;++i) C[i][0]=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=i;++j)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n-i+1;++j)
            inc(ans,(ll)fac[j]*fac[n-j]%mod*C[n-i][j-1]%mod*(i*(i-1))%mod*j%mod);
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(bzoj,组合数学,计数)