鸽子的糖

题目

题目描述
鸽子 α \alpha α 最近把 n n n 根棒棒糖拆了开来,正准备拼到一起…

α α α 现在十分苦恼,它现在需要把这 n n n 根糖拆出来的 n n n 个糖果和 n n n 根棒给重新组合一下,不过 α α α 并不知道该怎么组装好看,只能先列出几个条件:

  1. 所有糖果和棒都要用上。
  2. 每对糖果之间都必须直接或间接连接。
  3. 显然对于一个棒全部用上的情况,会形成一个环, α α α 觉得环上点的数量为 m m m 比较好。

现在 α α α 想问的是,有多少种满足上述几个条件的无向图呢?

注意,包在糖果上的包装纸上才有标号,而棒上是没有的。两个方案不同当且仅当对于某一个标号的糖果,在两个方案中与它相连的糖果的标号组成的集合不同,并且我们定义每个点的集合在一开始就有它本身(即形成自环的点不会影响方案数)。

答案对 998244353 998244353 998244353 取模。

输入格式
输入包括一行两个正整数 n n n m m m

输出格式
输出一行一个整数,表示方案数。

数据范围与约定
对于所有数据, 1 ≤ m ≤ n ≤ 3 × 1 0 3 1\le m\le n\le 3\times10^3 1mn3×103

思路

s u b t a s k    1 ( m = 1 ∨ m = 2 ) {\tt subtask\;1}(m=1\vee m=2) subtask1(m=1m=2)

发现就是求本质不同的树的数量。设 f ( n ) f(n) f(n) n n n 个点时本质不同的树的数量。显然 f ( 1 ) = 1 f(1)=1 f(1)=1 。有递推方程式:

f ( n ) = ∑ i = 1 n − 1 ( n − 2 i − 1 ) i ⋅ f ( i ) f ( n − i ) f(n)=\sum_{i=1}^{n-1}{n-2\choose i-1}i\cdot f(i)f(n-i) f(n)=i=1n1(i1n2)if(i)f(ni)

为什么是这样呢?发现我们的做法是 拆掉一条边再重新接上,为了不算重,我们规定把 1 1 1 作为树根,断开 n n n 与其父节点之间的连边。步骤如下:

  1. 在除了 1 1 1 n n n 以外的 n − 2 n{\tt -}2 n2 个点中选 i − 1 i{\tt -}1 i1 个点,与 1 1 1 一起组成 i i i 个点的树。方案 ( n − 2 i − 1 ) f ( i ) {n-2\choose i-1}f(i) (i1n2)f(i)
  2. 剩下的 n − i n{\tt-}i ni 个点组成一棵树,以 n n n 作为根节点。方案数为 f ( n − i ) f(n-i) f(ni)
  3. 选取 i i i 个点中的任意一个作为 n n n 的父节点。方案数为 i i i

乘法原理可得该递推式。


2019 / 11 / 28    u p d a t e {\tt 2019/11/28\; update} 2019/11/28update

只需要使用 p r u f e r \tt{prufer} prufer 序列就可以直接得到 f n = n n − 2 f_n=n^{n-2} fn=nn2 的方便打法——当然也可以用递推的方式验证。

果然我这种dp又差、学的又少的人就会被淘汰。


s u b t a s k    ∞ \tt{subtask\;\infty} subtask

模仿上面的想法,发现只改变点的数量,所以考虑用 g ( n ) g(n) g(n) 表示 n n n 个点组成环上有 m m m 个点的本质不同的基环树的数量( m m m 即输入中的 m m m 。边界条件 g ( m ) = ( m − 1 ) ! 2 g(m)=\frac{(m-1)!}{2} g(m)=2(m1)! ,递推式

g ( n ) = n n − m ∑ i = m n − 1 ( n − 1 i ) i    g ( i )    f ( n − i ) g(n)=\frac{n}{n-m}\sum_{i=m}^{n-1}{n-1\choose i}i\;g(i)\;f(n-i) g(n)=nmni=mn1(in1)ig(i)f(ni)

初状态 g ( m ) g(m) g(m) 就是每个点都在环上。考虑从 1 1 1 开始按顺序生成一个序列,有 ( m − 1 ) ! (m{\tt-}1)! (m1)! 种方案。但是顺时针和逆时针实际上是一样的,所以再除以 2 2 2 。即: 1 − 2 − 3 − 4 1{\tt-}2{\tt-}3{\tt-}4 1234 1 − 4 − 3 − 2 1{\tt-}4{\tt-}3{\tt-}2 1432 本质是一个环。画一画就知道了!

题外话:我最近在一本数学奥赛、讲组合数学专题的书上看到了这个玩意儿(下图左一)
在这里插入图片描述

其他的情况,还是拆掉一条边吧:

  • 1 1 1 不在环上,把环上的点都视做树根,拆掉 1 1 1 与其父节点的连边。那么就得从除去 1 1 1 ( n − 1 ) (n{\tt-}1) (n1) 个点中选 i i i 个点组成起始基环树,然后把 1 1 1 接上去(并不是一定要接到环上去,不然会算掉 1 1 1 不与环直接相连的情况)。答案是 X = ∑ i = m n − 1 ( n − 1 i ) i    g ( i )    f ( n − i ) X=\sum_{i=m}^{n-1}{n-1\choose i}i\;g(i)\;f(n{\tt-}i) X=i=mn1(in1)ig(i)f(ni)
  • 1 1 1 在环上的情况,将环上的点的点集记作 S S S ,就 s w a p ( 1 , v ) ( v ∈ S ) {\tt swap}(1,v)(v\in S) swap(1,v)(vS) 一下就行了!所以是 m X mX mX 。听上去很合理。

可是 m X mX mX 有重复的情况啊

考虑一个 1 1 1 在环上的情况,发现它的“前身”可以是 1 1 1 与非环上的 n − m n{\tt-}m nm 个点中任意一个点 s w a p \tt{swap} swap 得到的结果。
在这里插入图片描述
比如楼上的方案。它可以是这两种情况:
在这里插入图片描述
也就是说,每 n − m n{\tt-}m nm 个方案都是一样的!所以只有 m X ÷ ( n − m ) = m n − m X mX\div (n-m)=\frac{m}{n-m}X mX÷(nm)=nmmX 种情况。

由加法原理, a n s = m n − m X + X = m + ( n − m ) n − m X = n n − m X {\tt ans}=\frac{m}{n-m}X+X=\frac{m+(n-m)}{n-m}X=\frac{n}{n-m}X ans=nmmX+X=nmm+(nm)X=nmnX

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

const int MaxN = 3000, Mod = 998244353;
int f[MaxN+5], g[MaxN+5], c[MaxN][MaxN], n, m, inv[MaxN+5];

int main(){
    scanf("%d %d",&n,&m);
    for(int i=(inv[1]=1)+1; i<=n; ++i)
    	inv[i] = (0ll+Mod-Mod/i)*inv[Mod%i]%Mod;
    for(int i=0; i<n; ++i) // 杨辉三角 
        for(int j=c[i][0]=1; j<=i; ++j)
            c[i][j] = (c[i-1][j]+c[i-1][j-1])%Mod;
    f[1] = 1;
    for(int i=2; i<=n; ++i) for(int j=1; j<i; ++j)
        f[i] = (1ll*j*f[j]%Mod*f[i-j]%Mod*c[i-2][j-1]+f[i])%Mod;
    // 或者用f[i] = qkpow(i,i-2,Mod)也可以 
    if(m == 1 or m == 2){
        printf("%d",f[n]);
        return 0;
    }
    g[m] = 1;
    for(int i=3; i<m; ++i) // (m-1)!/2
        g[m] = (1ll*g[m]*i)%Mod;
    for(int i=m+1; i<=n; ++i){
        for(int j=m; j<i; ++j)
            g[i] = (1ll*j*g[j]%Mod*f[i-j]%Mod*c[i-1][j]+g[i])%Mod;
        g[i] = 1ll*g[i]*i%Mod*inv[i-m]%Mod;
    }
    printf("%d",g[n]);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值