[説明]ニワトリ生息(Pruferシーケンス番号+スターリング)のCJOI2019の集大成を
トピックの背景
舒服了。
タイトル説明
あなたはn個の点に無根の木を持って、各点は、ラベル(1〜n)を持っています。
今、あなたが知っている、m個のリーフノードの合計、異なる形式のプログラムを必要と木の数があります。
答えがする(9 + 10 ^ 7 \)\剰余を。:ここではいくつかの可能な便利な定義されている
リーフ:ポイント1度は。
異なる:同じ木の二つの数字であれば\(TL =(V、E_1)、T2 =(V、E_2)\)、\ (TL \ NEQ T2 \)とが存在する場合にだけ\((U、V)は\ E_1で、(U、V )\ E_2 notin \)入力形式
合計行は、最初の行は、mがポイントと葉の数の合計数を表し、2つの数値を含んでいるN。
データは、木を確保するために存在している必要があります。出力フォーマット
ライン整数、出力への回答(9 + 10 ^ 7 \)\モジュロ結果。
サンプル入力1
5 3
サンプル出力1
60
サブタスク
以下のための\(10 \%\)データ、保証する\(N、M <= 5
\)をする(20 \%\)\確実にするために、データを\(N、M <= 10
\)をする(40 \%を\ \)データを、確実にするために\(N、M <= 20
\)をする(60 \%の\)\確実にするために、データを\(N、M <= 5000
\)を追加するための\(10 \%の\)データ、確保\(M = 2 \)
の追加のために\(10 \%\)を保証するために、データを\(M = N-1 \の
) 追加のための\(10 \%\)を保証するために、データ\を(N、M> = 5 \)
に(100 \%の\)\確実にするために、データを\(N、M <= 2 \回10 ^ 5 \)を
\(溶液\)
木の列挙は、一Prufer配列によって変換され、第二スターリングPruferシーケンス番号に従って解決されます。
Pruferシーケンス
参照ツリーが得られると仮定\(T \)を、我々はこのような動作により、シーケンスを取得することができ、配列およびその対応する木が1です。つまり、任意の二つの異なる法的Pruferシーケンスは、二つの異なる木に対応させていただきます。彼はここに木が番号が付けられていることを指摘しました。
ツリーにおいて、リーフノードの最小数を選択し、親ノードは、それPrufer配列に加え、リーフノードを削除します。
2つだけのノードが(片側のみが同定されていない)、この時点で決定することができるまでツリー全体の形状を有しています。
そう数が得られる\(N-2 \)番目の配列を、この順序木形1に対応。この\(N-2 \)配列要素が構成することができる
[N ^ {N-2 \
} \] の組合せ。
対応の法則によると、それは言うことであるツリー内のNノードは無符号合計である\(N ^ {N-2 } \) の組み合わせ。
私たちは、シーケンス、それが表示された回数-1ノード内Pruferシーケンス中程度の重要性を見てください。だから今の質問はなった、私が保証します\(m個\) Pruferシーケンスを表示されませんノードを。
第二種のスターリング数
(\ \ {Bmatrixを}始めるN \ \\ m個\端{Bmatrix}) を表し、\(N-を\)要素はに分割される\(m個\)非空集合プログラム番号。
ここでのメッセージは表示されません:要素は、区別なしのコレクション異なっています。
漸化式
\ [{N \ブレースM} = {N-1 \ブレースM-1} + M {N-1 \ブレースM} \]
証明:参照してくださいyybのブログを。
包含と除外\(O(n)と\) 、またはNTT \(O(N \ N-ログ)\) (追求)
\ [S_2(N、M)= \ {Bmatrixを}始めるN \\ m個\端{Bmatrix} = \ FRAC 1 {M!} \ sum_ {i = 0} ^ M(-1)^ I {(MI) ^ N} {m個の\は、Iを選択} \]
証明:参照してくださいyybのブログを。
最終的な回答
の\ [(nm)を!\タイムズ
\回{N-2 \ブレースNM} \ {N Mを選択してください\}] 最後のボックス(セット)無差のスターリング数は、しかし、ここでは異なるため、それはで乗算されなければならないので、\((nm)を!\) 、それを構成しています。
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int mod=1e9+7;
const int maxn=2e5+5;
int jc[maxn];
int inv[maxn];
int n,m,ans,k;
typedef const int& ct;
inline int ksm(int base,ct p){
register int ret=1;
for(register int t=p;t;t>>=1,base=1ll*base*base%mod)
if(t&1) ret=1ll*ret*base%mod;
return ret;
}
int C(int n,int m){
if(n<m) return 0;
return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int S(ct n,ct m){
register int ret=0;
for(register int t=0;t<=m;++t)
if(t&1) ret=(0ll+ret-1ll*ksm(m-t,n)*C(m,t)%mod+mod)%mod;
else ret=(0ll+ret+1ll*ksm(m-t,n)*C(m,t)%mod+mod)%mod;
return 1ll*ret*inv[m]%mod;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("dfzjj.in","r",stdin);
freopen("dfzjj.out","w",stdout);
#endif
jc[0]=1;
inv[0]=1;
for(register int t=1;t<maxn;++t) jc[t]=1ll*jc[t-1]*t%mod,inv[t]=1ll*inv[t-1]*ksm(t,mod-2LL)%mod;
n=qr();m=qr();
ans=1ll*jc[n-m]*C(n,m)%mod*S(n-2,n-m)%mod;
printf("%d\n",ans);
return 0;
}