【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;
}


發佈了61 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章