题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1095
题意:
给你包含1~n的排列,初始位置1,2,3...,n,问你刚好固定前m个数中的k个数的位置,问你有多少中排列方案。(比如5 3 2有1 4 3 2 5这种方案,1和3固定了)
思路:
前m个取k个就是C(m, k)个方案。然后就是类似错排的思想,设dp[i]为i个数在初始位置各不相同。其中的组合数用逆元算出。
ans = dp[m - k] * C(n - m, 0) + dp[m - k + 1] * C(n - m, 1) .. dp[n - k] * C(n - m, n - m),这个式子表示取后面n-m个数的某些数 与 前面的m - k个数形成错排,剩下的数位置不变。
最后就是ans * C(m, k)
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 LL mod = 1e9 + 7, dp[1005]; 5 LL f[1005]; 6 LL fpow(LL a, LL n) { 7 LL ans = 1; 8 while(n) { 9 if(n & 1) 10 ans = ans * a % mod; 11 a = a * a % mod; 12 n >>= 1; 13 } 14 return ans; 15 } 16 int main() 17 { 18 dp[0] = 1; 19 dp[1] = 0, dp[2] = 1; 20 f[1] = f[0] = 1, f[2] = 2; 21 for(LL i = 3; i <= 1000; ++i) { 22 dp[i] = (dp[i - 1] + dp[i - 2]) % mod * (LL)(i - 1) % mod; 23 f[i] = f[i - 1] * (LL)i % mod; 24 } 25 int n, m, k, t; 26 scanf("%d", &t); 27 for(int ca = 1; ca <= t; ++ca) { 28 scanf("%d %d %d", &n, &m, &k); 29 if(n == m && m - k == 1) { 30 printf("Case %d: 0\n", ca); 31 continue; 32 } 33 LL ans = f[m] * fpow(f[k]*f[m - k]%mod, mod - 2) % mod; 34 int temp = m - k, temp2 = n - m; 35 LL res = 0; 36 for(int i = temp; i <= temp2 + temp; ++i) { 37 res = (res + dp[i] * f[temp2] % mod * fpow(f[i - temp]*f[temp2 - i + temp] % mod, mod - 2) % mod) % mod; 38 } 39 printf("Case %d: %lld\n", ca, ans * res % mod); 40 } 41 return 0; 42 }
时间: 2024-10-21 01:07:57