How Many Sets II

Description

给一个集合,一共 n 个元素,从中选取 m 个元素,满足选出的元素中没有相邻的元素,一共有多少种选法。(结果对 p 取模 \(1 <= p <= 10^9\)

Solution

关于新题目:我没有证完,或许不能这样做,这一段可以跳过。

新题目:给一个集合,一共 n 个元素,从中选取 m 个元素,满足选出的元素相隔的元素个数大于等于 k,一共有多少种选法。(结果对 p 取模 \(1 <= p <= 10^9\)

这种题目其实可以直接转换到上面的题目。

我们设 p 数组为选出来的元素的下标集合。定义 \(t[i]=p[i]-(i-1)*k\)

可以发现,每个 \(t[i]\)\(t[i-1]\) 之间的距离都会比 \(p[i]\)\(p[i-1]\) 的距离少了 k。(这个地方和【BalticOI 2004】Sequence 的转化是一样哒好吧其实我只是想骗访问量嘿嘿嘿

而我们先开始要求 \(p[i]-p[i-1]>=k+1\),所以现在就要求 \(t[i]-t[i-1]>=1\) 了。(然后,其实我就不会了)

好了我们回到原题。这里有一种非常巧妙的方法:我们定义年糕精为没有编号的相同的妖怪,一共有 n 个年糕精。先拿出 m 个年糕精,剩下的年糕精形成 \(n-m+1\) 个空位,我们再把 m 个年糕精放回空位就可以了。这样我们的公式就是 \(C(n-m+1,m)\)。(为什么要定义年糕精为没有编号的相同的妖怪?我们选择了年糕精的空位相当于选择了年糕精的编号,因为整个数列是要保持有序的)

Code

#include <cstdio>
typedef long long ll;

int n, m, mod;

int Inv(int x, int y) {
	int r = 1;
	while(y) {
		if(y & 1)  r = 1ll * r * x % mod;
		x = 1ll * x * x % mod; y >>= 1;
	}
	return r;
}

int C(const int n, const int m) {
	if(n < m) return 0;
	int up = 1, down = 1;
	for(int i = 1; i <= m; ++ i) {
		up = 1ll * up * (n - i + 1) % mod;
		down = 1ll * down * i % mod;
	}
	return 1ll * up * Inv(down, mod - 2) % mod;
}

int Lucas(const int n, const int m) {
	if(! m) return 1;
	if(n < m) return 0;
	return 1ll * Lucas(n / mod, m / mod) * C(n % mod, m % mod) % mod;
}

int main() {
	while(~ scanf("%d %d %d", &n, &m, &mod)) printf("%d\n", Lucas(n - m + 1, m));
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/AWhiteWall/p/12778723.html
今日推荐