原题传送门
黑题倒不至于,但是紫题至少了
首先输入的
a
,
b
a,b
a,b不用管,因为是求方案数
然后开始愉快的推公式
设从 a − > b a->b a−>b有 x x x条边,那么不包括 a a a , b ,b ,b,这条路上经过了 ( x − 1 ) (x-1) (x−1)个点
- 从 ( n − 2 ) (n-2) (n−2)个点中取出 ( x − 1 ) (x-1) (x−1)个点并且顺序一变,又是一种方案,因此用排列,方案数为 P n − 2 x − 1 P_{n-2}^{x-1} Pn−2x−1
- x x x条边长总和为 m m m,相当于把 m m m分成 x x x段,使用插板法,方案数为 C m − 1 x − 1 C_{m-1}^{x-1} Cm−1x−1
- 另外还有 ( n − 1 − x ) (n-1-x) (n−1−x)条边取值随意,只要在 [ 1 , m ] [1,m] [1,m]内就行,所以方案数为 m n − 1 − x m^{n-1-x} mn−1−x
- 另外还有一堆点,如何排列?我犯难了,推不出来啊,于是我
偷偷的去翻了一下题解,因为剩下的点要让他们组成一个森林,并将每棵树接到 a − > b a->b a−>b这条路径上,把问题转化为把 n n n个点分成 ( x + 1 ) (x+1) (x+1)棵树的森林,并且 ( x + 1 ) (x+1) (x+1)个点不属于任何一棵树
引入 C a y l e y 定 理 Cayley定理 Cayley定理: n n n 个标号节点形成一个有 k k k 颗树的森林,使得给定的 k k k 个点没有两个点属于同一颗树的方案数为 k ∗ n n − k − 1 k*n^{n-k-1} k∗nn−k−1,然后 k k k用 ( x + 1 ) (x+1) (x+1)代入,变成 ( x + 1 ) ∗ n n − x − 2 (x+1)*n^{n-x-2} (x+1)∗nn−x−2
然后用乘法原理乘起来, f ( x ) f(x) f(x)表示路径中有 x x x条边的方案数
f ( x ) = P n − 2 x − 1 ∗ C m − 1 x − 1 ∗ m n − 1 − x ∗ ( x + 1 ) ∗ n n − x − 2 f(x)=P_{n-2}^{x-1}*C_{m-1}^{x-1}*m^{n-1-x}*(x+1)*n^{n-x-2} f(x)=Pn−2x−1∗Cm−1x−1∗mn−1−x∗(x+1)∗nn−x−2
= ( n − 2 ) ! ( n − x − 1 ) ! ∗ ( m − 1 ) ! ( m − x ) ! ∗ ( x − 1 ) ! ∗ m n − 1 − x ∗ ( x + 1 ) ∗ n n − x − 2 =\frac{(n-2)!}{(n-x-1)!}*\frac{(m-1)!}{(m-x)!*(x-1)!}*m^{n-1-x}*(x+1)*n^{n-x-2} =(n−x−1)!(n−2)!∗(m−x)!∗(x−1)!(m−1)!∗mn−1−x∗(x+1)∗nn−x−2
预处理:
p
o
w
e
r
n
[
i
]
=
n
i
,
p
o
w
e
r
m
[
i
]
=
m
i
,
f
a
c
[
i
]
=
i
!
,
i
n
v
[
i
]
=
1
i
!
在
模
域
的
逆
元
powern[i]=n^i,powerm[i]=m^i,fac[i]=i!,inv[i]=\frac{1}{i!}在模域的逆元
powern[i]=ni,powerm[i]=mi,fac[i]=i!,inv[i]=i!1在模域的逆元
枚举每个x,用加法原理把答案加起来即可
细节特判:
- x = n − 1 x=n-1 x=n−1时,是一条链,无法用Cayley定理,方案数看成1
- 注意 C m − 1 x − 1 C_{m-1}^{x-1} Cm−1x−1中 ( x − 1 ) > ( m − 1 ) (x-1)>(m-1) (x−1)>(m−1)情况,此时方案数为0
细节真麻烦
Code:
#include <bits/stdc++.h>
#define maxn 1000010
#define LL long long
#define qy 1000000007
using namespace std;
LL fac[maxn], inv[maxn], powern[maxn], powerm[maxn], n, m, no_use, ans;
inline LL read(){
LL s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
LL ksm(LL n, LL k){
if (!k) return 1;
LL sum = ksm(n, k >> 1);
sum = sum * sum % qy;
if (k & 1) sum = sum * n % qy;
return sum;
}
int main(){
n = read(), m = read(), no_use = read(), no_use = read();
powern[0] = powerm[0] = 1;
for (int i = 1; i <= n; ++i) powern[i] = powern[i - 1] * n % qy, powerm[i] = powerm[i - 1] * m % qy;
fac[0] = fac[1] = 1; LL N = max(n, m);
for (int i = 2; i <= N; ++i) fac[i] = 1LL * fac[i - 1] * i % qy;
inv[N] = ksm(fac[N], qy - 2);
for (int i = N - 1; i >= 0; --i) inv[i] = 1LL * inv[i + 1] * (i + 1) % qy;
for (int i = 1; i < n; ++i){
LL P = fac[n - 2] * inv[n - i - 1] % qy, C = i > m ? 0 : fac[m - 1] * inv[m - i] % qy * inv[i - 1] % qy;
LL sum = P * C % qy * powerm[n - 1 - i] % qy;
ans = (ans + (i == (n - 1) ? sum : sum * (i + 1) % qy * powern[n - 2 - i] % qy)) % qy;
}
printf("%lld\n", ans);
return 0;
}

本文深入解析了一道紫题的算法思路,通过推导公式来计算特定路径上的方案数,涉及组合数学中的排列、组合及Cayley定理的应用,详细介绍了预处理和动态规划的技巧。
6182

被折叠的 条评论
为什么被折叠?



