zoukankan      html  css  js  c++  java
  • [BZOJ 4517][Sdoi2016]排列计数(组合数学/错排公式)

    Description

    求有多少种长度为 n 的序列 A,满足以下条件:
    1 ~ n 这 n 个数在序列中各出现了一次
    若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
    满足条件的序列可能很多,序列数对 10^9+7 取模。

    Solution

    其实是一个裸的错排啊,用F[n]来表示n个元素的错排方案,F[n]=(n-1)*(F[n-1]+F[n-2])

    感性地理解一下这个公式大概是这样的:

    如果我们把第n个元素放在第k个位置(k有n-1种取值)

    1.把第k个元素也放在第n个位置,则有F[n-2]种方案【把剩下的元素错排】

    2.把第k个元素放在其他位置,则有F[n-1]种方案【也可以看做把1~n-1的元素错排,然后交换n和k】

    理性的证明我也不会= =

    于是这题的答案就是C(n,m)*F[n-m]

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #define Mod 1000000007
    #define MAXN 1000005
    typedef long long LL;
    using namespace std;
    int T,n,m;
    LL fac[MAXN],inv[MAXN],F[MAXN];
    int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    void init()
    {
        fac[0]=1,inv[1]=1;
        for(int i=1;i<MAXN;i++)
        fac[i]=(fac[i-1]*i)%Mod;
        for(int i=2;i<MAXN;i++)
        inv[i]=((Mod-Mod/i)*inv[Mod%i])%Mod;
        inv[0]=1;
        for(int i=1;i<MAXN;i++)
        inv[i]=(inv[i]*inv[i-1])%Mod;
        F[0]=1,F[1]=0;
        for(int i=2;i<MAXN;i++)
        F[i]=((i-1)*(F[i-1]+F[i-2])%Mod)%Mod;
    }
    LL C(LL x,LL y)
    {
        if(x<y)return 0;
        return ((fac[x]*inv[y])%Mod*inv[x-y])%Mod;
    }
    int main()
    {
        T=read(),init();
        while(T--)
        {
            n=read(),m=read();
            printf("%lld
    ",(C(n,m)*F[n-m])%Mod);
        }
        return 0;
    }
  • 相关阅读:
    无题
    【HNOI 2002 】营业额统计
    P1589
    【网络流24题】最长递增子序列
    【NOI2008】志愿者招募
    【NOI2015】软件包管理器
    P1347
    【BZOJ 3262三维偏序】陌上花开
    数论六·模线性方程组
    数论五·欧拉函数
  • 原文地址:https://www.cnblogs.com/Zars19/p/6979273.html
Copyright © 2011-2022 走看看