我是靠谱客的博主 纯情眼睛,最近开发中收集的这篇文章主要介绍2018.10.25 bzoj4517: [Sdoi2016]排列计数(组合数学),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

传送门
组合数学简单题。


A n s = ( n m ) ∗ 1 Ans=binom {n} {m}*1 Ans=(mn)1~ ( n − m ) (n-m) (nm)的错排数。
前面的直接线性筛逆元求。
后面的错排数递推式本蒟蒻竟然推出来了。
首先说说为什么 A n s = ( n m ) ∗ 1 Ans=binom {n} {m}*1 Ans=(mn)1~ n n n- m m m的错排数。
考虑首先选出 m m m个排列正确的数有 ( n m ) binom {n} {m} (mn)种选法。
然后剩下的 n − m n-m nm个数因为有严格的大小关系相当于只需要保证每个数与其下标不相同。
那么我们把这 n − m n-m nm个数提出来。
它们的错排数跟 1 1 1~ n n n- m m m的错排数是相同的。
因此就是是这样了。
所以错排数怎么推呢?
假设已经求出了 1 , 1 1,1 1,1~ 2 , 1 2,1 2,1 ~ 3 3 3 1 1 1 ~ n n n- 1 1 1的错排数,要求 1 1 1~ n n n的错排数。
我们设 1 1 1~ i i i的错排数为 f [ i ] f[i] f[i]
考虑现在在某个排列 1 1 1~ i − 1 i-1 i1中加入 i i i ( i ≥ 2 ) (i geq 2) (i2)
那么有两种情况。

  1. 已有的排列中排列正确的数个数为0,那么只用从原排列中随便选个数放到第 i i i个位置,然后拿 i i i去填空就行了,方案数为 ( i − 1 ) ∗ f [ i − 1 ] (i-1)*f[i-1] (i1)f[i1]
  2. 已有的排列中排列正确的数个数为1,那么把这个数挪到第 i i i个位置,然后用 i i i去填空就行了,由于 i − 1 i-1 i1个数都有可能成为那个排列正确的数,而且对于剩下的 i − 2 i-2 i2个数都是错排的,因此方案数为 ( i − 1 ) ∗ f [ i − 2 ] (i-1)*f[i-2] (i1)f[i2]
    => f [ i ] = ( i − 1 ) ∗ ( f [ i − 1 ] + f [ i − 2 ] ) f[i]=(i-1)*(f[i-1]+f[i-2]) f[i]=(i1)(f[i1]+f[i2])

代码:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int mod=1e9+7,N=1e6+5;
int T,n,m,f[N],ifac[N],fac[N];
int main(){
T=read();
f[0]=1,f[1]=0,fac[0]=fac[1]=ifac[1]=ifac[0]=1;
for(int i=2;i<=N-5;++i)fac[i]=1ll*fac[i-1]*i%mod,ifac[i]=1ll*(mod-mod/i)*ifac[mod%i]%mod,f[i]=1ll*(f[i-1]+f[i-2])*(i-1)%mod;
for(int i=2;i<=N-5;++i)ifac[i]=1ll*ifac[i]*ifac[i-1]%mod;
while(T--)n=read(),m=read(),printf("%dn",1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod*f[n-m]%mod);
return 0;
}

最后

以上就是纯情眼睛为你收集整理的2018.10.25 bzoj4517: [Sdoi2016]排列计数(组合数学)的全部内容,希望文章能够帮你解决2018.10.25 bzoj4517: [Sdoi2016]排列计数(组合数学)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部