【题解】CF1109D:Sasha and Interesting Fact from Graph Theory

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

原题传送门
黑题倒不至于,但是紫题至少了
首先输入的 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) (x1)个点

  • ( n − 2 ) (n-2) (n2)个点中取出 ( x − 1 ) (x-1) (x1)个点并且顺序一变,又是一种方案,因此用排列,方案数为 P n − 2 x − 1 P_{n-2}^{x-1} Pn2x1
  • x x x条边长总和为 m m m,相当于把 m m m分成 x x x段,使用插板法,方案数为 C m − 1 x − 1 C_{m-1}^{x-1} Cm1x1
  • 另外还有 ( n − 1 − x ) (n-1-x) (n1x)条边取值随意,只要在 [ 1 , m ] [1,m] [1,m]内就行,所以方案数为 m n − 1 − x m^{n-1-x} mn1x
  • 另外还有一堆点,如何排列?我犯难了,推不出来啊,于是我偷偷的去翻了一下题解 ,因为剩下的点要让他们组成一个森林,并将每棵树接到 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} knnk1,然后 k k k ( x + 1 ) (x+1) (x+1)代入,变成 ( x + 1 ) ∗ n n − x − 2 (x+1)*n^{n-x-2} (x+1)nnx2

然后用乘法原理乘起来, 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)=Pn2x1Cm1x1mn1x(x+1)nnx2
= ( 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} =(nx1)!(n2)!(mx)!(x1)!(m1)!mn1x(x+1)nnx2

预处理:
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=n1时,是一条链,无法用Cayley定理,方案数看成1
  • 注意 C m − 1 x − 1 C_{m-1}^{x-1} Cm1x1 ( x − 1 ) > ( m − 1 ) (x-1)>(m-1) (x1)>(m1)情况,此时方案数为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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值