CodeForces【组合数学】1109D-Sasha and Interesting Fact from Graph Theory

CodeForces 1109D Sasha and Interesting Fact from Graph Theory

◇题目传送门◆

题目大意

给定 N N N个结点,编号为 1 , 2 , … , N 1,2,\ldots,N 1,2,,N,再给定两个点 A , B A,B A,B,要求这两个点的距离为 M M M,且树中的每一条边权均为 1 1 1 M M M的整数,求这样能构造的树的方案数。

分析

显然可以知道 A , B A,B A,B是什么值对整个答案的计算没有影响。

考虑按如下方法构造:

即先构造出一条有 x x x个结点链,再在这条链上随意连接子树。

这个链的方案数为 A N − 2 x − 1 A_{N-2}^{x-1} AN2x1

那么在这条链上,分配的方案数为 C M − 1 x − 1 C_{M-1}^{x-1} CM1x1

这一部分的方案数为 A N − 2 x − 1 ⋅ C M − 1 x − 1 A_{N-2}^{x-1}\cdot C_{M-1}^{x-1} AN2x1CM1x1

考虑计算剩余的结点的方案数:

利用Prufer序列的性质,我们将构造出来的链顺序标上 N − x , N − x + 1 , ⋯   , N N-x,N-x+1,\cdots,N Nx,Nx+1,,N,这样一来,将我们构造出的树的Prufer序列的最后 x − 2 x-2 x2位一定就是我们所构造出来的链。

那么考虑前 N − x N-x Nx个结点,它们在序列中的顺序是随意的,不难得出这 N − x N-x Nx个结点随意排列的方案数为 N N − x − 2 N^{N-x-2} NNx2。此外,又由于这个序列中除了最后一个点是固定的,这个序列的剩余的点必定是任意值,所以这个方案数为 ( x + 1 ) ⋅ N N − x − 2 (x+1)\cdot N^{N-x-2} (x+1)NNx2

好像网上有些说可以用广义Cayley定理来解决这个问题。。。

最后考虑边权,因为我们事先已经固定了 x x x条边的边权,其他的边随意选取即可,所以这个方案数就是 M N − x − 1 M^{N-x-1} MNx1

所以最后的总方案数为 ∑ i = 1 N − 1 A N − 2 x − 1 ⋅ C M − 1 x − 1 ⋅ ( x + 1 ) ⋅ N N − x − 2 ⋅ M N − x − 1 \sum_{i=1}^{N-1}{}A_{N-2}^{x-1}\cdot C_{M-1}^{x-1}\cdot(x+1)\cdot N^{N-x-2}\cdot M^{N-x-1} i=1N1AN2x1CM1x1(x+1)NNx2MNx1

此外,注意到 i = N − 1 i=N-1 i=N1时有一项的次数是负数,特判一下就是了。

参考代码

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;
const int Maxn = 1e6;
const int Mod = 1e9 + 7;

ll fac[Maxn + 5], inv_fac[Maxn + 5];
ll QuickPow(ll a, ll k) {
	ll ret = 1;
	while(k) {
		if(k & 1) ret = ret * a % Mod;
		a = a * a % Mod;
		k >>= 1;
	}
	return ret;
}
void Init() {
	fac[0] = inv_fac[0] = 1;
	for(int i = 1; i <= Maxn; i++)
		fac[i] = fac[i - 1] * i % Mod;
	inv_fac[Maxn] = QuickPow(fac[Maxn], Mod - 2);
	for(int i = Maxn - 1; i >= 1; i--)
		inv_fac[i] = inv_fac[i + 1] * (i + 1) % Mod;
}
ll C(int n, int m) {
	if(n < 0 || m < 0 || n < m) return 0;
	return fac[n] * inv_fac[m] % Mod * inv_fac[n - m] % Mod;
}
ll A(int n, int m) {
	if(n < 0 || m < 0 || n < m) return 0;
	return fac[n] * inv_fac[n - m] % Mod;
}

int N, M;

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	Init();
	int t1, t2;
	scanf("%d %d %d %d", &N, &M, &t1, &t2);
	ll ans = 0;
	for(int i = 1; i < N; i++) {
		ll tmp = A(N - 2, i - 1) * C(M - 1, i - 1) % Mod;
		tmp = tmp * QuickPow(M, N - i - 1) % Mod;
		if(i != N - 1) tmp = tmp * QuickPow(N, N - i - 2) % Mod * (i + 1) % Mod;
		ans = (ans + tmp) % Mod;
	}
	printf("%lld\n", ans);
	return 0;
}
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值