ZOJ 3688

来源:互联网 发布:vs mysql 编辑:程序博客网 时间:2024/06/14 14:12

题解:

很显然利用容斥原理,对于k个章节放错的排列方法,因为放错章节的序列为1 2 2 3 3 4,...,n,1,要满足从这个序列中取出k个数,k个数不想邻,可知即递增序列,且两个1不能同时取到,可以证明自增序列的个数为C(n-k+1,k),而1同时取到的个数为C(2n-4-(k-2)+1,k-2),于是可解

需要注意的是求解逆元的时候要用到快速幂,而且初始化要将fac[n]的每一个数的逆元也要初始化,否则会T


代码:

#include <cstdio>#include <cstring>#define ll long long#define mod 1000000007using namespace std;const int MAX=200010;ll fac[MAX],inv[MAX];ll quickPow(ll n,ll k){    ll ans=1;    while(k)    {        if(k&1)            ans=ans*n%mod;        n=n*n%mod;        k=k>>1;    }    return ans;}void pre(){    fac[0]=1;    for(int i=1;i<=200000;i++)        fac[i]=fac[i-1]*i%mod;    for(int i=1;i<=200000;i++)        inv[i]=quickPow(fac[i],mod-2);    inv[0]=quickPow(fac[0],mod-2);}ll cal(ll n,ll k){    return fac[n]*inv[k]%mod*inv[n-k]%mod;}int main(){    ll n;    pre();    while(scanf("%lld",&n)!=EOF)    {        ll ans=fac[n];        if(n==1)        {            printf("0\n");            continue;        }        for(int k=1;k<=n;k++)        {            if(k==1)                ans=(ans-cal(n,k)*2*fac[n-1]%mod)%mod;            else if(k&1)                ans=(ans-(cal(2*n-k+1,k)-cal(2*n-k-1,k-2))*fac[n-k]%mod)%mod;            else                ans=(ans+(cal(2*n-k+1,k)-cal(2*n-k-1,k-2))*fac[n-k]%mod)%mod;        }        printf("%lld\n",(ans%mod+mod)%mod);    }    return 0;}