BZOJ-4517: [Sdoi2016]排列计数

[文章目录]

Description

求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 10^9+7 取模。
T=500000,n≤1000000,m≤1000000

第一道BZ的blog,好激动啊。(看了大师%%%的blog

递推+组合数。

先有m个a[i]=i的方案为Cnm个。然后乘上个n-m个都不在自己的位置上的方案数。

假设f[x]表示x个数都不在自己位置上的方案数。考虑f[x+1].

如果新来的元素在m位置上,而m元素在n的位置上,f[x+1]=(n-1)*f[n-2].

如果m不在n的位置上,就说明除了n以外其他的所有n-1个元素都有一个位置不能选,和f[]数组的定义相同,所以f[x+1]=(n-1)*f[n-1].

注:(n-1)为n选择的位置的种类数。

所以就有递推式f[x]=(x-1)*(f[x-1]+f[x-2]).

考虑n和m的范围:100 0000所以进行预处理。

然后,因为mod的是一个质数,所以乘法逆元搞一下组合数就可以了。(amod-2为a的逆元)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
LL t,n,m;
LL mod=1000000007;
LL C[1001000],f[1001000];
LL pow(LL x)
{
	LL re=1,y=mod-2;
	while(y)
	{
		if(y&1) re=re*x%mod;
		x=x*x%mod;y>>=1;
	}
	return re;
}
int main()
{
	scanf("%lld",&t);
	C[0]=1;C[1]=1;C[2]=2;f[2]=1;f[0]=1;
	for(LL i=3;i<=1000000;i++)
	{
		C[i]=C[i-1]*i%mod;
		f[i]=(i-1)*(f[i-1]+f[i-2])%mod;
	}
	while(t--)
	{
		scanf("%lld%lld",&n,&m);
		printf("%lld\n",f[n-m]*C[n]%mod*pow(C[m])%mod*pow(C[n-m])%mod);
	}
	return 0;
}

忘写  \n WA了两次  QAQ  

发表评论

邮箱地址不会被公开。 必填项已用*标注