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;
}