ZOJ 2688 The Review Plan II

https://zoj.pintia.cn/problem-sets/91827364500/problems/91827369470

题意:

n天n个计划,一天完成一个计划,第i个计划不能在第i天和第i+1天完成,第n个计划不能在第n天和第1天完成,求安排计划的方案数。

有禁区的排列问题

在n*n有禁区棋盘上放n个棋子,每行每列只能放1个,第i行的禁区为第i和i+1列,第n行禁区为第n和1列

根据容斥原理,得 方案数=n! - r1(n-1)! + r2(n-2)! - …… ± rn

其中ri表示把i个旗子放在禁区的方案数

禁区可以看做是由2n个数组成的圆环

求ri相当于求在圆环上选i个不相邻的数的方案数

根据定理从{1,2,3……n}中选r个不相邻的数的组合,其组合数为C(n-k+1,k)

可以推出从圆环上选i个不相邻的数的组合,方案数为 C(2n-k+1,k)-C(2n-k-1,k-2)

#include<cstdio>

using namespace std;

const int mod=1e9+7;
#define N 200001

int inv[N],fac[N];

int pow(int a,int b)
{
    int p=1;
    for(;b;b>>=1,a=1LL*a*a%mod)
        if(b&1) p=1LL*p*a%mod;
    return p;
}

int C(int n,int k)
{
     return 1ll*fac[n]*inv[k]%mod*inv[n-k]%mod;
}

int main()
{
     fac[0]=fac[1]=1;
     for(int i=2;i<N;++i) fac[i]=1ll*fac[i-1]*i%mod;
     inv[0]=inv[1]=1;
     for(int i=2;i<N;++i) inv[i]=pow(fac[i],mod-2); 
     int n,ans;
     long long t;
     while(scanf("%d",&n)!=EOF)
     {
         if(n==1) 
         {
              printf("0
");
              continue;
         }
         ans=fac[n]-1LL*n*2*fac[n-1]%mod;
          t=1;
         for(int i=2;i<=n;++i,t=-t)
          {
              ans+=t*fac[n-i]*(C(2*n-i+1,i)-C(2*n-i-1,i-2))%mod;
              ans%=mod;
         }
          if(ans<0) ans+=mod;
         printf("%d
",ans);    
    }
    return 0;
}
         
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/12244674.html