本文主要是介绍BZOJ4517:排列计数(错排公式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
从开始看这题到现在,已经过了30多把lol的时间了。
话说今天又有一道排列计数的题让我懵逼。
题面
题意:问有多少长为n的排列a,恰好有m个位置存在a[i]=i。
我们枚举这n-m个a[i]≠i位置,有 Cmn 种情况。
对于x个数的排列,不存在a[i]=i的方案数设为f[x]。经过简单的打表可以发现
就可以水出这题了。
但是像千反田一样好奇的我非常好奇,学习了一下机巧的二项式反演。
我曾听说过广义容斥原理
讲的是对于一个含有n个条件的集合,全集为P。f[i]为恰好满足i个条件的方案数,w[S]为满足集合S中条件的方案数,g[S]为S的元素个数。公式为
我们设
就有
再回想h的计算方法,有
变形一下(我并不懂怎么变形,但每条柿子我都会证明),有
再有
证一下最后一条吧
由于对于杨辉三角的每一行,奇数项之和等于偶数项之和,故
有n个集合,假若设任意i个集合的并集大小都为f[i],任意i个集合的补集的并集的大小都为h[i](不知道这样的集合存不存在),就有
对于错排,设f[i]为i个数错排的方案。
显然有
反演过来就有
终于打完了,嘟嘟噜。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int nn=1000000,N=1003000;
const int p=1e9+7;int T,n,m;
LL jc[N],Ijc[N],I[N],f[N];int main()
{jc[0]=Ijc[0]=I[1]=1;for(int i=2;i<=nn;i++)I[i]=(p-p/i)*I[p%i]%p;for(int i=1;i<=nn;i++)jc[i]=jc[i-1]*i%p;for(int i=1;i<=nn;i++)Ijc[i]=Ijc[i-1]*I[i]%p;f[0]=1;f[1]=0;for(int i=2;i<=nn;i++)if(i&1)f[i]=(f[i-1]*i-1+p)%p;elsef[i]=(f[i-1]*i+1)%p;cin>>T;while(T--){scanf("%d%d",&n,&m);LL ans=jc[n]*Ijc[m]%p*Ijc[n-m]%p*f[n-m]%p;printf("%lld\n",ans);}return 0;
}
还是纱雾的水题专场
这篇关于BZOJ4517:排列计数(错排公式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!