TCO14 3B 1000:TreeDistance (拉格朗日插值+矩阵树)

题解:
把原图的边看作白边,其他边看作黑边做矩阵树。
根据矩阵树的意义,最后结果为所有边权的乘积,我们扩域把白边边权看作 1 1 ,黑边看做 x x ,那么最后得到的多项式 k ≤ k 的项即为答案。

不过直接做太慢了,用 FFT F F T 也只能做到 n4logn n 4 log ⁡ n ,考虑拉格朗日插值,做 n n 次矩阵树最后插出原多项式即可,时间复杂度 O(n4) O ( n 4 )

const int N=55,mod=1e9+7;
inline int add(int x,int y) {return (x+y>=mod)?x+y-mod:x+y;}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b) {
    int rs=1;
    for(;b;b>>=1, a=mul(a,a)) if(b&1) rs=mul(rs,a);
    return rs;
}
int n,fa[N],yc[N],f[N],g[N],a[N][N];
inline int det() {
    for(int i=1;ifor(int j=1;jif(a[i][j]<0) a[i][j]+=mod;
    int sgn=1;
    for(int i=1;iint l;
        for(l=i;lif(l==n) return 0;
        if(l!=i) {
            sgn=-sgn;
            for(int j=i;jint inv=power(a[i][i],mod-2);
        for(int j=i+1;jint t=mul(a[j][i],inv);
            for(int k=i;kint rs=1;
    for(int i=1;ireturn (sgn>0)?rs:mod-rs;
}
inline int calc(int x) {
    memset(a,0,sizeof(a));
    for(int i=1;i<=n;i++) 
        for(int j=i+1;j<=n;j++){
            if(fa[i]==j || fa[j]==i)
                --a[i][j],--a[j][i],++a[i][i],++a[j][j];
            else 
                a[i][j]-=x,a[j][i]-=x,a[i][i]+=x,a[j][j]+=x;
        }
    return det();
}
class TreeDistance {
    public:
    int countTrees(vector<int> p, int K) {
        n=p.size()+1;
        for(int i=2;i<=n;i++) fa[i]=p[i-2]+1;
        for(int i=1;i<=n;i++) yc[i]=calc(i);
        for(int i=1;i<=n;i++) {
            memset(g,0,sizeof(g));
            g[0]=1;
            for(int j=1;j<=n;j++) {
                if(i==j) continue;
                int inv=power(add(i,mod-j),mod-2);
                for(int k=n;k>=0;k--) 
                    g[k]=add(mul(g[k],mul(mod-j,inv)),k?mul(g[k-1],inv):0);
            }
            for(int j=0;j<=n;j++) 
                f[j]=add(f[j],mul(g[j],yc[i]));
        }
        for(int i=1;i<=n;i++)
            f[i]=add(f[i-1],f[i]);
        return f[min(K,n)];
    }
};

你可能感兴趣的:(矩阵树定理,拉格朗日插值法)