[BZOJ 4517][Sdoi2016]排列计数(组合数学/错排公式)
Description
求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 10^9+7 取模。
Solution
其实是一个裸的错排啊,用F[n]来表示n个元素的错排方案,F[n]=(n-1)*(F[n-1]+F[n-2])
感性地理解一下这个公式大概是这样的:
如果我们把第n个元素放在第k个位置(k有n-1种取值)
1.把第k个元素也放在第n个位置,则有F[n-2]种方案【把剩下的元素错排】
2.把第k个元素放在其他位置,则有F[n-1]种方案【也可以看做把1~n-1的元素错排,然后交换n和k】
理性的证明我也不会= =
于是这题的答案就是C(n,m)*F[n-m]
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #define Mod 1000000007 #define MAXN 1000005 typedef long long LL; using namespace std; int T,n,m; LL fac[MAXN],inv[MAXN],F[MAXN]; int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void init() { fac[0]=1,inv[1]=1; for(int i=1;i<MAXN;i++) fac[i]=(fac[i-1]*i)%Mod; for(int i=2;i<MAXN;i++) inv[i]=((Mod-Mod/i)*inv[Mod%i])%Mod; inv[0]=1; for(int i=1;i<MAXN;i++) inv[i]=(inv[i]*inv[i-1])%Mod; F[0]=1,F[1]=0; for(int i=2;i<MAXN;i++) F[i]=((i-1)*(F[i-1]+F[i-2])%Mod)%Mod; } LL C(LL x,LL y) { if(x<y)return 0; return ((fac[x]*inv[y])%Mod*inv[x-y])%Mod; } int main() { T=read(),init(); while(T--) { n=read(),m=read(); printf("%lld\n",(C(n,m)*F[n-m])%Mod); } return 0; }