【BZOJ4517】【Sdoi2016】排列计数 线性逆元 错位排列

首先真诚地感谢BZ某大神为我们解锁正确的备考姿势。一语惊醒梦中人。

回到题目。。。不难看出一答案ans=D[n-m]*C(m,n);,其中D是错位排序数,也就是n个数全排列中,满足ai!=i的排列的个数,具体证明涉及到容斥原理。存在递推公式。

为了计算C,我们可以将n! mod p以及 (n!) ^ -1 mod p 全部预处理出来,两个操作均存在线性递推。

然后答案就可以在O(1)内求解了。。。

#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define mo 1000000007
#define maxn 1000005
long long jie[maxn],inv[maxn],D[maxn];
void ready()
{
	jie[0]=jie[1]=1;for(int i=2;i<=1000000;i++)jie[i]=(jie[i-1]*i)%mo;
	inv[0]=inv[1]=1;for(int i=2;i<=1000000;i++)inv[i]=(mo-(mo/i))*inv[mo%i]%mo;
	for(int i=2;i<=1000000;i++)inv[i]=(inv[i-1]*inv[i])%mo;
	D[0]=1;D[1]=0;D[2]=1;for(int i=2;i<=1000000;i++)D[i]=(D[i-1]+D[i-2])*(i-1)%mo;
	return ;
}
int T;
long long n,m,ans;
void _readLL(long long &x)
{
	x=0; char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
	return ;
}
char s[35];int cc;
void out(long long x)
{
	if(x==0)putchar('0');
	cc=0; while(x){cc++; s[cc]=x%10+'0';x=x/10;}
	while(cc){putchar(s[cc]);cc--;}
	
	return ;
}
int main()
{
	freopen("in.txt","r",stdin);
	ready();
	scanf("%d",&T);
	while(T--)
	{
		_readLL(n);_readLL(m);
		ans=(D[n-m]*jie[n])%mo;
		ans=ans*inv[m]%mo*inv[n-m]%mo;
		if(m<=n)out(ans);
		if(T)putchar('\n');
	}
	return 0;
}


你可能感兴趣的:(【BZOJ4517】【Sdoi2016】排列计数 线性逆元 错位排列)