BZOJ4517:排列计数(错排公式)

17 篇文章 0 订阅

从开始看这题到现在,已经过了30多把lol的时间了。
话说今天又有一道排列计数的题让我懵逼。

题面
题意:问有多少长为n的排列a,恰好有m个位置存在a[i]=i。

我们枚举这n-m个a[i]≠i位置,有 Cmn 种情况。
对于x个数的排列,不存在a[i]=i的方案数设为f[x]。经过简单的打表可以发现

f[i]=f[i1]i+(1)i

就可以水出这题了。

但是像千反田一样好奇的我非常好奇,学习了一下机巧的二项式反演。
我曾听说过广义容斥原理
讲的是对于一个含有n个条件的集合,全集为P。f[i]为恰好满足i个条件的方案数,w[S]为满足集合S中条件的方案数,g[S]为S的元素个数。公式为

f[r]=SP(1)g[S]rCrg[S]w[S]

我们设
h[x]=g[S]=xw[S]

就有
f[r]=i=rn(1)irCrih[i]

再回想h的计算方法,有
h[r]=i=rnCrif[i]f[r]=i=rn(1)irCrih[i]

变形一下(我并不懂怎么变形,但每条柿子我都会证明),有
h[r]=i=0rCirf[i]f[r]=i=0r(1)riCirh[i]

再有
h[r]=i=0r(1)iCirf[i]f[r]=i=0r(1)iCirh[i]

证一下最后一条吧
i=0r(1)iCirf[i]

=i=0r(1)iCirj=0i(1)jCjih[j]

=i=0rj=0i(1)i(1)jCirCjih[j]

=i=0rj=0i(1)ijCjrCijrjh[j]

=j=0rCjrh[j]i=jr(1)ijCijrj

由于对于杨辉三角的每一行,奇数项之和等于偶数项之和,故
=f[r]

有n个集合,假若设任意i个集合的并集大小都为f[i],任意i个集合的补集的并集的大小都为h[i](不知道这样的集合存不存在),就有

h[r]=i=0r(1)iCirf[i]
f[r]=i=0r(1)iCirh[i]

对于错排,设f[i]为i个数错排的方案。
显然有

i=0nCinf[i]=n!

反演过来就有
f[n]=i=0n(1)niCini!

=n!i=0n(1)ii!

终于打完了,嘟嘟噜。

#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;
    else
    f[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;
}

这里写图片描述
还是纱雾的水题专场

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值