[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 ;
}

posted @ 2018-10-10 11:39  beretty  阅读(146)  评论(0编辑  收藏  举报