[説明] CF917Dのストレンジャー木(prufer配列+二項反転)

[説明] CF917Dのストレンジャー木(prufer配列+二項反転)

呼ばれるものがある考えてみましょう\(prufer \)シーケンス、およびというもの、図ユニコムの数が

その後、我々は最初のカウントを検討し、少なくとも\(Kに\)エッジは、私たちは木のDPを使用することができますプログラム内のプログラムの数を

\(DP(I、J、 K)\) のためにあることを意味\(I \)ノードとそのサブツリーの合計(J \)は\エッジが選択され、そして、および\(I \)合計\(K \)番目ユニコムポイントがブロックである\(\ PRODのSIZ [] \ ) の合計。転送は、単純な友人です。(使用メモリフレンドリーにCFメモリアクセスが、遅い!言葉遣い)

(DP()\)\容易である見つけることができる(F(K)\)\少なくともことを示している\(K \)プログラム内のプログラムにおける辺の数。

私たちは、設定(G(K)\)\、そして考える答えです\(F(K)\)\(G(k)は\)の関係

我々が持っているので、我々厳選選択エッジが選択されたプログラムであり、リング、木のいずれかの側は、正当ありませんので、それは、注目すべきことは、我々は、ツリーにあるので、サイドを選択することである
\ [fは( K)= \ sum_ {J \
GEのK} \] G(J){jは\ Kを選択} 反転に係る二項直接得られる
\ [G(K)= \ sum_ {J \ GE kを} {J \選択K}( - 1)^ { KJ} F(J)\]

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<assert.h>

using namespace std;  typedef long long ll; 
inline int qr(){
    int ret=0,f=0,c=getchar();
    while(!isdigit(c))f|=c==45,c=getchar();
    while(isdigit(c)) ret=ret*10+c-48,c=getchar();
    return f?-ret:ret;
}

const int maxn=105;
const int mod=1e9+7;
int dp[maxn][maxn][maxn],n,siz[maxn],ans[maxn];
int invs[maxn*maxn],c[maxn][maxn];
int inv[maxn][maxn*maxn];
vector<int>e[maxn];
void add(int fr,int to){e[fr].push_back(to);e[to].push_back(fr);}
int MOD(const int&x){return x>=mod?x-mod:x;}
int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
int MOD(const int&x,const int&y,const int&z,const int&b){return 1ll*x*y%mod*b%mod*z%mod;}
int ksm(const int&ba,const int&p){
    int ret=1;
    for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
        if(t&1) ret=MOD(ret,b);
    return ret;
}

void pre(const int&n){
    invs[1]=1;
    for(int t=2;t<=n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]),assert(MOD(t,invs[t])==1);
    for(int t=n+1;t<=n*n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]);
    for(int t=1;t<=n*n;++t)
        for(int i=1;i<=n;++i)
            inv[i][t]=MOD(i,invs[t]);
    for(int t=0;t<=n;++t)
        for(int i=c[t][0]=1;i<=t;++i)
            c[t][i]=MOD(c[t-1][i-1]+c[t-1][i]);
    //cerr<<"c[5][2]="<<c[5][2]<<endl;
}

void dfs(const int&now,const int&last){
    dp[now][0][1]=1;
    siz[now]=1;
    for(auto t:e[now]){
        if(t^last){
            dfs(t,now);
            int g[maxn][maxn];
            memset(g,0,sizeof g);
            /*
            for(int i=siz[now]-1;~i;--i){// edge_totol
                for(int j=0,edj=min(siz[t]-1,i);j<=edj;++j){// edge_target
                    for(int p=1,edp=siz[t];p<=edp;++p){// vertices_target
                        if(dp[t][j][p]){
                            for(int k=siz[now];k;--k){// vertices_total
                                int&s=g[i][k];
                                //connect
                                if(i-j-1>=0&&k>p) s=(s+1ll*dp[now][i-j-1][k-p]*dp[t][j][p]%mod*inv[k][(k-p)*p])%mod;
                                //dont connect
                                if(i>=j) s=(s+1ll*dp[now][i-j][k]*dp[t][j][p])%mod;
                            }
                        }
                    }
                }
            }这种写法很不优秀,内存访问非常不连续!
            */
            for(int i=0;i<siz[now];++i)
                for(int k=1;k<=siz[now];++k)
                    for(int j=0;j<siz[t];++j)
                        for(int p=1;p<=siz[t];++p){
                            ll l=1ll*dp[now][i][k]*dp[t][j][p]%mod;
                            g[i+j+1][k+p]=(g[i+j+1][k+p]+l*inv[k+p][k*p])%mod;
                            g[i+j][k]=MOD(g[i+j][k]+l);
                        }
            siz[now]+=siz[t];
            memcpy(dp[now],g,sizeof g);
        }
    }
}

int rt,val=1e9;
void dfsrt(int now,int last){
    siz[now]=1;
    int k=0;
    for(auto t:e[now])
        if(!siz[t])
            siz[now]+=siz[t],k=max(k,siz[t]);
    k=max(k,n-siz[now]);
    if(k<val) rt=now;
}

int main(){
    n=qr();
    pre(101);
    for(int t=1;t<n;++t) add(qr(),qr());
    int k=min(n,55);
    dfs(k,0);
    for(int e=0;e<=n-1;++e){
        for(int i=1;i<=e+1;++i)
            ans[e]=MOD(ans[e]+dp[k][e][i]);
        if(e<=n-2) ans[e]=MOD(ksm(n,n-2-e),ans[e]);
        if(e==n-1) ans[e]=1;
    }
    for(int t=0;t<=n-1;++t){
        int ret=0;
        for(int i=t;i<=n-1;++i){
            int d=MOD(c[i][t],ans[i]);
            if((t^i)&1) ret=MOD(ret-d+mod);
            else ret=MOD(ret+d);
        }
        cout<<ret<<' ';
    }
    cerr<<endl;
    return 0;
}

おすすめ

転載: www.cnblogs.com/winlere/p/12240444.html