Luogu P6191 [USACO09FEB]Bulls And Cows S (牡牛和牝牛)

发布时间 2023-06-14 15:55:57作者: Joker__King

[USACO09FEB]Bulls And Cows S

题目背景

一年一度的展会要来临了,Farmer John 想要把 \(N\)\(1 \leq N \leq 100,000\))只奶牛和公牛安排在单独的一行中。 John 发现最近公牛们非常好斗;假如两只公牛在这一行中靠的太近,他们就会吵架,以至于斗殴,破坏这和谐的环境。

题目描述

John 非常的足智多谋,他计算出任何两只公牛之间至少要有 \(K\)\(0 \leq K \lt N\))只奶牛,这样才能避免斗殴。John 希望你帮助他计算一下有多少种安排方法,可避免任何斗殴的的发生。John 认为每头公牛都是一样的,每头奶牛都是一样的。因而,只要在一些相同的位置上有不同种类的牛,那这就算两种不同的方法。

输入格式

两个整数 \(N\)\(K\)

输出格式

输出约翰可以安排的方法数。考虑到这个数可能很大,你只要输出对 \(5\,000\,011\) 取模之后的结果就可以了。

样例 #1

样例输入 #1

4 2

样例输出 #1

6

提示

下面的就是 FJ 思考出可行的 6 种方案(C 代表奶牛,B 代表公牛):

  • CCCC
  • BCCC
  • CBCC
  • CCBC
  • CCCB
  • BCCB

刚开始不会写这道题,翻了翻奆佬们的题解,发现都是什么仅考虑相对位置间的关系,根本看不懂啊QAQ呜呜呜,所以诞生了我这篇相对好理解的题解


前置芝士(摘自百度文库)

image
image
image


题解

首先前面的部分和大部分排列组合的题解相同

设有 \(i\) 个公牛,那么需要在中间插入的奶牛数是 \((i-1) * k\)

因此可以得到剩下的牛的数量是总的牛的数量减去公牛和母牛的数量,即 \(n-i-(i-1) * k\)

我们现在就要考虑把剩下的牛全变成奶牛插入每个公牛的空隙之间,即插入到 \(i-1\) 个空隙里

此时我们显然可以把问题转变为把 \(n-i-(i-1) * k\) 个相同的小球,放到 \(i-1\) 个不同的盒子里,盒子可以为空

此时前置芝士就用到辣!!!

将前置芝士里面的 \(n\) 带入成我们的剩下的奶牛个数 \(n-i-(i-1) * k\) ,盒子个数 \(m\) 带入成 \(i-1\) 然后化简.

最后即可得出与其他排列组合题解相同的式子 \(\Sigma^{n} _ {i=0}C^{i} _ {n-(i-1) * k}\)

记得特判有可能位置不够选,到 $ i>n-(i-1) * k$ 时退出

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const long long mod = 5000011;
long long n, k, ans = 0;
long long inv[52113140], fac[52113140];

int main() {
	cin >> n >> k;
	fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for (int i = 2; i <= mod; ++ i) {
		fac[i] = fac[i - 1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	}
	for (int i = 2; i <= mod; ++ i) {
		inv[i] = inv[i - 1] * inv[i] % mod;
	}
	for (int i = 1; i <= n; ++ i) {
		int cow = (i - 1) * k;
		int remain = n - cow;
		if (i > remain) break;
		ans += (fac[remain] * inv[remain - i] % mod * inv[i]) % mod;
		ans %= mod;
	}
	cout << (ans + 1) % mod << endl;
	return 0;
}