这是 2021 2021 2021 年的第一篇博客,让我们走进
数论
和树论
的天地吧!
洛谷P6862 [RC-03] 随机树生成器 \color{green}{\texttt{洛谷P6862 [RC-03] 随机树生成器}} 洛谷P6862 [RC-03] 随机树生成器
[Problem] \color{blue}{\texttt{[Problem]}} [Problem]
千万注意不是对 ( 1 × 1 0 9 + 7 ) \left (1 \times 10^9+7\right ) (1×109+7) 而是对 ( 1 × 1 0 9 + 9 ) \left (1 \times 10^9+9\right ) (1×109+9) 取模!
[Solution] \color{blue}{\texttt{[Solution]}} [Solution]
首先,让我们先算出一共有多少种可能的树。
我们把一般的父亲找儿子的建树过程反过来,即儿子找父亲,这两种方法是一样的,因为当每个节点都确定了自己的父亲是谁的时候,这棵树就建好了。
第 1 1 1 个点一定是根,只有 1 1 1 种选择;对于第 i i i 个点( 2 ≤ i ≤ n 2 \leq i \leq n 2≤i≤n)而言,节点 [ 1 , i ) [1,i) [1,i) 都可以是它父亲,有 ( i − 1 ) (i-1) (i−1) 种选择,故不同的树的数量为:
( ∏ i = 2 n ( i − 1 ) ) × 1 = ( n − 1 ) ! \left (\prod\limits_{i=2}^{n} (i-1)\right ) \times 1 =(n-1)! (i=2∏n(i−1))×1=(n−1)!
然后,让我们考虑答案是多少。
对于节点 i i i 而言( 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n),节点 ( i , n ] (i,n] (i,n] 都可能成为它的儿子。特别地,节点 j ( j ∈ ( i , n ] ) j(j \in (i,n]) j(j∈(i,n]) 有 1 j − 1 \dfrac{1}{j-1} j−11 的概率成为 i i i 的儿子。所以它对节点的度的总和的贡献即为:
( n − 1 ) ! × 1 j − 1 (n-1)! \times \dfrac{1}{j-1} (n−1)!×j−11
所以答案为:
( n − 1 ) ! × ( ( ∑ j = i + 1 n 1 j − 1 ) + 1 ) (n-1)! \times \left ( \left ( \sum \limits_{j=i+1}^{n} \dfrac{1}{j-1} \right ) +1\right ) (n−1)!×((j=i+1∑nj−11)+1)
那个 + 1 +1 +1 是因为还有一条从 i i i 的父亲到 i i i 的边。当然对于节点 1 1 1 而言就不需要 + 1 +1 +1。
因为有多组数据和取模,所以我们预处理出在取模意义下的 ( n − 1 ) ! (n-1)! (n−1)! 和逆元的前缀和,就可以 O ( 1 ) O(1) O(1) 地回答每个询问了。
总的时间复杂度: O ( max { n } + T ) O(\max \{n\}+T) O(max{n}+T)。
[code] \color{blue}{\texttt{[code]}} [code]
const int N=1e7+100;
const int mod=1e9+9;
inline int ksm(int a,int b){
register int ret=1;
while (b){
if (b&1) ret=1ll*ret*a%mod;
a=1ll*a*a%mod;b>>=1;
}
return ret;
}
int inv[N],fac[N],pre[N];
inline void InitImportantArray(int n){
fac[0]=inv[1]=1;pre[1]=0;
for(int i=1;i<=n;i++){
fac[i]=1ll*fac[i-1]*i%mod;
if (i>1) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<=n;i++)
pre[i]=(pre[i-1]+inv[i-1])%mod;
}
int main(){
InitImportantArray(1e7);
for(int t=1,T=read();t<=T;t++){
register int n=read(),k=read();
if (k==1) printf("%lld\n",1ll*fac[n-1]*(pre[n]-pre[k]+mod)%mod);
else printf("%lld\n",1ll*fac[n-1]*(pre[n]-pre[k]+1+mod)%mod);
}
return 0;
}