1. 程式人生 > >【SDOI 2016】 排列計數

【SDOI 2016】 排列計數

include bits span 因此 遞推公式 tar printf for 遞推

【題目鏈接】

https://www.lydsy.com/JudgeOnline/problem.php?id=4517

【算法】

有m個數在原來的位置上,說明有(n-m)個數不再原來的位置上

那麽,我們可以選出(n-m)個數,使這(n-m)個數都不在原來的位置上,再讓剩下的m個數都在原來的位置上

錯位排列遞推公式 :

f(1) = 0

f(2) = 1

f(n) = (n - 1)(f(n-1) + f(n-2)) (n >= 2)

因此,答案為C(n,n-m)f(n-m)

預處理錯位排列數,階乘和階乘逆元,即可

【代碼】

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000010
const int P = 1e9 + 7;

int T,n,m,ans;
int f[MAXN],fac[MAXN],inv[MAXN];

inline int power(int a,int n)
{
        int res = 1,b = a;
        while (n)
        {
                
if (n & 1) res = 1ll * res * b % P; b = 1ll * b * b % P; n >>= 1; } return res; } inline void init() { int i; f[0] = 1; f[1] = 0; f[2] = 1; for (i = 3; i < MAXN; i++) f[i] = 1ll * (i - 1) * (f[i-1] + f[i-2
]) % P; fac[0] = 1; for (i = 1; i < MAXN; i++) fac[i] = 1ll * fac[i-1] * i % P; inv[MAXN-1] = power(fac[MAXN-1],P-2); for (i = MAXN - 2; i >= 0; i--) inv[i] = 1ll * inv[i+1] * (i + 1) % P; } inline int C(int n,int m) { if (n < m) return 0; if (m == 0) return 1; return 1ll * fac[n] * inv[m] % P * inv[n-m] % P; } int main() { init(); scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); ans = 1ll * C(n,n-m) * f[n-m] % P; printf("%d\n",ans); } return 0 }

【SDOI 2016】 排列計數