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\n"); 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\n",ans); } return 0; }