2021.01.02日常总结——2021第一记

这是 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 2in)而言,节点 [ 1 , i ) [1,i) [1,i) 都可以是它父亲,有 ( i − 1 ) (i-1) (i1) 种选择,故不同的树的数量为:

( ∏ i = 2 n ( i − 1 ) ) × 1 = ( n − 1 ) ! \left (\prod\limits_{i=2}^{n} (i-1)\right ) \times 1 =(n-1)! (i=2n(i1))×1=(n1)!

然后,让我们考虑答案是多少。

对于节点 i i i 而言( 1 ≤ i ≤ n 1 \leq i \leq n 1in),节点 ( 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} j11 的概率成为 i i i 的儿子。所以它对节点的度的总和的贡献即为:

( n − 1 ) ! × 1 j − 1 (n-1)! \times \dfrac{1}{j-1} (n1)!×j11

所以答案为:

( 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 ) (n1)!×((j=i+1nj11)+1)

那个 + 1 +1 +1 是因为还有一条从 i i i 的父亲到 i i i 的边。当然对于节点 1 1 1 而言就不需要 + 1 +1 +1

因为有多组数据和取模,所以我们预处理出在取模意义下的 ( n − 1 ) ! (n-1)! (n1)! 和逆元的前缀和,就可以 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值