Luogu P4071[SDOI2016]排列计数

Posted gjy-juruo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P4071[SDOI2016]排列计数相关的知识,希望对你有一定的参考价值。

题目大意:

求有多少种 (1)(n) 的排列 (a),满足序列恰好有 (m) 个位置 (i),使得 (a_i=i),答案对 (10^{9}+7)

正文:

可以先列个表:

技术图片

再从题目意思出发,若 (m=0),即没有一个数字在自己位置上,那就是错位排列。错位排列的递推式是 (f_i=(i-1)(f_{i-1}+f{i-2}))。而我们在列表的过程中会发现,特殊情况除外,除在自己位置上的 (m) 个数,其它的数进行错位排列。也就是说 最终答案 = 确定排列的数的总值 * 其它数错位排列。

问题来了,确定排列的数的总值是多少?我们不妨举举例,设 (n=4,m=2),那么就有 ({1,2,x,x},{1,x,3,x},{1,x,x,4},{x,2,3,x},{x,2,x,4},{x,x,3,4}).总共就有 (C_{n}^{m})

代码:

ll _pow(ll a, int b){
    a %= p;
	ll ans = 1;
	for(; b; b >>= 1, a = a * a % p)
		if(b & 1)
			ans = ans * a % p;
    return ans;
}
void init()
{
	prod[0] = 1;
	for (register int i = 1; i <= 1000000; i++)
		prod[i] = (prod[i - 1] * i) % p,
		inv[i] = _pow(prod[i], p - 2);
	a[2] = 1;
	for (register int i = 3; i <= 1000000; i++)
		a[i] = (i - 1) * ((a[i - 1] + a[i - 2]) % p) % p;
}

int main()
{
	int t;
	init();
	for (scanf("%d", &t); t--;)
	{
		scanf("%d%d", &n, &m);
		if(m == 0)
		{
			printf("%lld
", a[n]);
			continue;
		}
		if(n == m){puts("1");continue;}
		if(n - 1 == m){puts("0");continue;}  //图表中的几个特殊情况
		printf("%lld
", (prod[n] * inv[m] % p * inv[n-m] % p) * a[n-m] % p); //括号内的是组合数,a数组是错位排列个数
	}
	return 0;
}

以上是关于Luogu P4071[SDOI2016]排列计数的主要内容,如果未能解决你的问题,请参考以下文章

[SDOI2016]排列计数

bzoj 4517: [Sdoi2016]排列计数

[SDOI2016]排列计数

[BZOJ4517][SDOI2016]排列计数(错位排列)

BZOJ 4517: [Sdoi2016]排列计数

BZOJ4517SDOI2016排列计数 [数论]