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

来源:互联网 发布:如何ping网络端口号 编辑:程序博客网 时间:2024/05/19 15:19

首先真诚地感谢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 1000005long 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;}


0 0
原创粉丝点击