#lydlx03x0B04. 排列计数

排列计数

稳定排列计数

题目描述

求有多少种长度为 nn 的序列 AA,满足以下条件:

  1. 1n1 \sim nnn 个数在序列中各出现了一次(即 AA11nn 的一个排列)
  2. 若第 ii 个数 A[i]A[i] 的值为 ii,则称 ii 是稳定的
  3. 序列恰好有 mm 个数是稳定的

由于满足条件的序列可能很多,所以请你将序列数对 109+710^9+7 取模后输出。

输入格式

第一行一个数 TT,表示有 TT 组数据。

接下来 TT 行,每行两个整数 n,mn, m

输出格式

输出 TT 行,每行一个整数,表示求出的序列数对 109+710^9+7 取模后的值。

数据范围

  • T500000T \leq 500000
  • n1000000n \leq 1000000
  • m1000000m \leq 1000000

输入样例

5
1 0
1 1
5 2
100 50
10000 5000

输出样例

0
1
20
578028887
60695423

样例解释

第一个测试:n=1,m=0n=1, m=0

  • 只有 1 个数的排列:[1][1]
  • 稳定位置:位置1的值是1,所以有1个稳定位置
  • 要求恰好0个稳定位置,不可能
  • 输出:0

第二个测试:n=1,m=1n=1, m=1

  • 排列:[1][1]
  • 稳定位置:1个
  • 要求恰好1个稳定位置,符合
  • 输出:1

第三个测试:n=5,m=2n=5, m=2

  • 需要计算:从5个位置中选出2个作为稳定位置,其余3个位置都不能是稳定的(即错排)
  • 公式:C(5,2)×D(3)C(5, 2) \times D(3)
    • C(5,2)=10C(5, 2) = 10(组合数)
    • D(3)=2D(3) = 2(3个元素的错排数:2种)
    • 总数:10×2=2010 \times 2 = 20
  • 输出:20

第四、五个测试:大数计算

  • 需要模 109+710^9+7 计算
  • 输出取模后的结果

问题分析

这个问题是带有指定数量不动点的排列计数问题。

关键概念

  1. 排列1n1 \sim n 每个数恰好出现一次
  2. 稳定位置(不动点)A[i]=iA[i] = i 的位置 ii
  3. 错排:没有稳定位置的排列

计算公式

要求恰好有 mm 个稳定位置的排列数:

  1. nn 个位置中选出 mm 个作为稳定位置:C(n,m)C(n, m)
  2. 剩下的 nmn-m 个位置必须形成错排(没有任何位置是稳定的):D(nm)D(n-m)

因此,总数为:

Answer(n,m)=C(n,m)×D(nm)\text{Answer}(n, m) = C(n, m) \times D(n-m)

其中:

  • C(n,m)C(n, m) 是组合数
  • D(k)D(k)kk 个元素的错排数

错排数公式

kk 个元素的错排数 D(k)D(k) 有递推公式:

D(k)=(k1)×[D(k1)+D(k2)]D(k) = (k-1) \times [D(k-1) + D(k-2)]

边界条件:

  • D(0)=1D(0) = 1(空排列算一种错排)
  • D(1)=0D(1) = 0(1个元素不可能错排)

组合数计算

组合数 C(n,m)C(n, m) 需要模 109+710^9+7 计算:

C(n,m)=n!m!(nm)!(mod109+7)C(n, m) = \frac{n!}{m!(n-m)!} \pmod{10^9+7}

由于 109+710^9+7 是质数,可以使用阶乘逆元预处理。

算法设计

考虑到数据范围:

  • TT 最多 500,000 组查询
  • n,mn, m 最多 1,000,000

需要预处理

  1. 阶乘数组 fact[i] = i! mod MOD
  2. 阶乘逆元数组 inv_fact[i] = (i!)^{-1} mod MOD
  3. 错排数组 derange[i] = D(i) mod MOD

然后对于每组查询 (n,m)(n, m)

  • 如果 m>nm > n,答案为 0
  • 否则,答案为 C(n, m) * derange[n-m] mod MOD

特殊情况

  • n=mn = m 时:D(0)=1D(0) = 1,所以答案是 C(n,n)=1C(n, n) = 1
  • m=0m = 0 时:答案就是错排数 D(n)D(n)
  • m<0m < 0m>nm > n 时:答案为 0

时空限制

  • 时间限制:2秒
  • 空间限制:256MB