bzoj4517 [Sdoi2016]排列计数
Description
求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 10^9+7 取模。
Input
第一行一个数 T,表示有 T 组数据。
接下来 T 行,每行两个整数 n、m。
T=500000,n≤1000000,m≤1000000
Output
输出 T 行,每行一个数,表示求出的序列数
Sample Input
5
1 0
1 1
5 2
100 50
10000 5000
1 0
1 1
5 2
100 50
10000 5000
Sample Output
0
1
20
578028887
60695423
1
20
578028887
60695423
正解:组合数学。
这题要用到错排公式,虽然做这题之前我不知道这是什么,不过最后我还是自己欧出来了。。
首先,我们容易得到,答案就是$\binom{n}{m}$乘上错排公式(即$i$不在第$i$个位置上的数的方案数)。
然后这个错排随便欧一下就出来了啊。。
用二项式反演证明,可得:$g(n)=\sum_{k=0}^{n}(-1)^{n-k}\binom{n}{k}f(k)$
然后预处理出来,就没了。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define rhl (1000000007) 15 #define inf (1<<30) 16 #define N (1000010) 17 #define il inline 18 #define RG register 19 #define ll long long 20 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 21 22 using namespace std; 23 24 ll fac[N],f[N],g[N]; 25 int n,m; 26 27 il int gi(){ 28 RG int x=0,q=1; RG char ch=getchar(); 29 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 30 if (ch=='-') q=-1,ch=getchar(); 31 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 32 return q*x; 33 } 34 35 il void pre(){ 36 fac[0]=fac[1]=f[1]=g[1]=g[2]=1,fac[2]=f[2]=2; 37 for (RG int i=3;i<N;++i){ 38 fac[i]=fac[i-1]*i%rhl; 39 g[i]=(g[i-1]*i%rhl-f[i-3]+rhl)%rhl; 40 f[i]=g[i]*i%rhl; 41 } 42 return; 43 } 44 45 il ll qpow(RG ll a,RG ll b){ 46 RG ll ans=1; 47 while (b){ 48 if (b&1) ans=ans*a%rhl; 49 a=a*a%rhl,b>>=1; 50 } 51 return ans; 52 } 53 54 il ll cm(RG int n,RG int m){ 55 return fac[n]*qpow(fac[m],rhl-2)%rhl*qpow(fac[n-m],rhl-2)%rhl; 56 } 57 58 int main(){ 59 File("sequence"); 60 RG int T=gi(); pre(); 61 while (T--){ 62 n=gi(),m=gi(); if (n==m){ puts("1"); continue; } 63 printf("%lld\n",cm(n,m)*f[n-m-1]%rhl); 64 } 65 return 0; 66 }