P4071 [SDOI2016]排列计数 题解
简要题意:
求有多少种 \(1\) 到 \(n\) 的排列 \(a\),满足序列恰好有 \(m\) 个位置 \(i\),使得 \(a_i = i\).
答案对 \(10^9 + 7\) 取模。
分析:
首先考虑是哪 \(m\) 个位置,于是这有 \(\dbinom{n}{m}\) 种情况。
然后就是在 \(n-m\) 个位置上错位排序的情况。这不就是数列 \(D\)?
\(D(n)\) 表示使得 \(a_i \not = i (\forall i \in [1,n])\) 的长度为 \(n\) 的排列个数。
学过组合数学的大概都知道吧,显然我们有
\[D(n) = n D(n-1) + (-1)^n
\]
于是递推。
组合数的话,预处理阶乘和逆元就好了。
时间复杂度:\(\mathcal{O}(T \log n + n)\).
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+1;
typedef long long ll;
const ll MOD=1e9+7;
int T; ll n,m;
ll D[N],inv[N];
inline ll pw(ll x,ll y) {
ll ans=1; while(y) {
if(y&1) ans=ans*x%MOD;
x=x*x%MOD; y>>=1;
} return ans;
}
inline ll C(ll x,ll y) {
return inv[x]*pw(inv[y]*inv[x-y]%MOD,MOD-2)%MOD;
}
int main() {
scanf("%d",&T);
D[0]=1; inv[0]=1;
for(int i=1;i<N;i++) D[i]=(i*D[i-1]+pw(-1,i))%MOD;
for(int i=1;i<N;i++) inv[i]=inv[i-1]*i%MOD;
while(T--) {
scanf("%lld %lld",&n,&m);
// printf("%lld %lld\n",C(n,m),D[n-m]);
printf("%lld\n",C(n,m)*D[n-m]%MOD);
}
return 0;
}
简易的代码胜过复杂的说教。