[SDOI2016]排列计数
题目描述
求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 \(10^9+7\) 取模。
输入输出格式
输入格式:
第一行一个数 T,表示有 T 组数据。
接下来 T 行,每行两个整数 n、m。
输出格式:
输出 T 行,每行一个数,表示求出的序列数
输入输出样例
输入样例#1:
5
1 0
1 1
5 2
100 50
10000 5000
输出样例#1:
0
1
20
578028887
60695423
错排+组合数
显然答案是\(C(n,m) * f[n - m]\)
但是当时我把错排公式忘了
就现打表找了个好像不是很一样的递推式
$ if (i是偶数) f[i] = f[i - 1] * i + 1$
$ else f[i] = f[i - 1] * i - 1$
也过掉了这道题
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define int long long
const int M = 1000005 ;
const int mod = 1e9 + 7 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , m , Ans ;
int f[M] , fac[M] ;
void exgcd(int a , int b , int &x , int &y) {
if(b == 0) { x = 1 , y = 0 ; return ; }
exgcd(b , a % b , x , y) ;
int tmp = x ;
x = y ;
y = tmp - a / b * y ;
}
inline int inv(int a) {
int x , y ;
exgcd(a , mod , x , y) ;
return (x + mod) % mod ;
}
inline int C(int n , int m) {
if(m > n) return 0 ;
return (fac[n] * inv(fac[m] * fac[n - m]) + mod) % mod ;
}
# undef int
int main() {
# define int long long
f[0] = 1 ; f[1] = 0 ; fac[0] = 1 ; fac[1] = 1 ;
for(int i = 2 ; i <= 1000000 ; i ++) {
if(i % 2 == 0) f[i] = (f[i - 1] * i + 1 + mod) % mod ;
else f[i] = (f[i - 1] * i - 1 + mod) % mod ;
fac[i] = (fac[i - 1] * i) % mod ;
}
int T = read() ;
while(T --) {
n = read() ; m = read() ;
if(m > n) Ans = 0 ;
else Ans = (C(n , m) * f[n - m] + mod) % mod ;
printf("%lld\n",Ans) ;
}
return 0 ;
}