【Codeforces 1109D】树的计数+广义Caylay定理+排列组合

一道非常有趣的树的计数数学题。 传送门

题意

所有边权都是 [1,m] 中的整数的所有 n 个点的树中,点 a 到点 b 的距离恰好是 m 的有几个。 n m <= 1e6 首先,a和b具体是多少显然不重要。然后,我们枚举a和b之间有多少个点(不包括a,b),那么,a,b之间边数i+1条,对于每条边选边权,隔板法,有$C_{m-1}^{i} $种方法,从n-2个点中选取i个点,并且他们有顺序之分,那么就有$A_{n-2}^{i} $种方法,同时,对于剩余的n-2-i条边各自有m种取法,那么就有$m^{n-2-i} $种方法,最后我们就只需要看树的计数了。 广义Caylay定理传送门(wiki) 简单说下就是对于带标号树的总方案$n^{n-2} $,对于森林$ (n+1)^{n-1} $(我们可以看做多了一个虚点,将每棵原本树的根连向虚点)。而对于有k个不同联通分量的森林计算,T(k,n),就为$ k * n^{n-k-1} $(可以考虑到对于树的情况$n^{n-2} $是广义Caylay定理的特殊情况。 那么对于本题,就是i+2个联通分量,就应该在乘上$ (i+2) * n^{ n -(i+2)-1 }$ 最后答案 $ \sum\limits_{i=0}^{n-2} C_{n-2}^i * C_{m-1}^i * m^{n-2-i} * n^{n-3-i} * ( i! ) * (i+2) $,预处理逆元一遍算完。 code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;
const int mod = 1e9+7;
const int maxn = 1e6+5;
int add(int x,int y) { x+=y; return x>=mod?x-mod:x; }
int sub(int x,int y) { x-=y; return x<0?x+mod:x; }
int mul(int x,int y) { return 1ll*x*y%mod; }
int ksm(int a,int b) {
    if(b<0) b+=mod-1;
    int ans = 1;
    for(;b;b>>=1,a=mul(a,a))
        if(b&1) ans = mul(ans,a);
    return ans;
}
int fac[maxn],inv[maxn];
int n,m,a,b;
int getc(int a,int b) {
    if(a<b) return 0;
    return mul(fac[a],mul(inv[b],inv[a-b]));
}
int main() {
    scanf("%d%d%d%d",&n,&m,&a,&b);
    fac[0] = 1; for(int i=1;i<=max(n,m);i++) fac[i]=mul(fac[i-1],i);
    inv[max(n,m)] = ksm(fac[max(n,m)],mod-2); for(int i=max(n,m)-1;i>=0;i--) inv[i]=mul(inv[i+1],i+1);
    int ans = 0;
    for(int i=0;i<=n-2;i++) {
        ans = add(ans,mul(getc(n-2,i),mul(getc(m-1,i),
        mul(ksm(m,n-2-i),mul(ksm(n,n-3-i),mul(fac[i],i+2)) ))));
    }
    printf("%d",ans);
}
posted @ 2019-02-23 22:01  Newuser233  阅读(15)  评论(0编辑  收藏  举报