LOJ2034「SDOI2016」排列计数 排列组合

问题描述

求有多少种长度为 $ n $ 的序列 $ A $,满足以下条件:

  • $ 1 \sim n $ 这 $ n $ 个数在序列中各出现了一次;
  • 若第 $ i $ 个数 $ A_i $ 的值为 $ i $,则称 $ i $ 是稳定的。序列恰好有 $ m $ 个数是稳定的。

满足条件的序列可能很多,序列数对 $ 10 ^ 9 + 7 $ 取模。


题解

错排记数

\(n\) 封信装进 \(n\) 个信封,全部装错了,方案数。

\[d_i=(i-1) \times (d_{i-1}+d_{i-2}) \]

边界: \(d_1=0,d_2=1,d_3=2\)


首先处理 \(m\) 个相同的位置,显然方案数为 \(C_n^m\)

所以答案为 \(C_n^m \times d_{n-m} \mod 10^9+7\)


\(\mathrm{Code}\)

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

const int maxn = 1000000 + 7;
const int maxm = 2000000 + 7;
const int mod = 1000000007;

int T;
int n, m;

LL f[maxn], inv[maxn];
LL d[maxn];

LL fpow(LL x, int p) {
	LL res = 1ll;
	while(p) {
		if(p & 1) res = res * x % mod;
		x = x * x % mod, p >>= 1;
	}
	return res;
}

template < typename Tp >
void read(Tp &x) {
	x = 0;char ch = 1;int fh = 1;
	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if(ch == '-') fh = -1, ch=getchar();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	x *= fh;
}

void Init(void) {
	scanf("%d", &T);
}

void Preprocess(void) {
	f[0] = 1ll, d[2] = 1, d[3] = 2;
	for(int i = 1; i <= 1000000; i++) {
		f[i] = f[i - 1] * i % mod;
		inv[i] = fpow(f[i], mod - 2);
		if(i >= 4) d[i] = (i - 1) * (d[i - 1] + d[i - 2]) % mod;
	}
}

void Work(void) {
	Preprocess();
	while(T--) {
		scanf("%d%d", &n, &m);
		if(n - m == 1) puts("0");
		else if(m == n) puts("1");
		else if(m == 0) printf("%lld\n", d[n]);
		else printf("%lld\n", f[n] * inv[m] % mod * inv[n - m] % mod * d[n - m] % mod);
	}
}

int main() {
	Init();
	Work();
	return 0;
}
posted @ 2020-03-17 21:29  览遍千秋  阅读(153)  评论(0编辑  收藏  举报