圆排列:
问题:有n个不同的人,问围成一桌吃饭,问有多少种相对位置不同的做法;
分析:已知线排列是n! ,那么将首位连起来就是圆排列,每旋转一个人的度数对应一个新的线排列,可选择n次,也就是说每个圆排列较线排列来说出现了n次,那么圆排列的个数为n! / n = (n-1)!.
题:https://ac.nowcoder.com/acm/problem/19857
题意:问n对cp坐不相邻的方案数。
分析:对于这类题,我们可以转化为恰好n对情侣不相邻的方案,再转化至少和不多于,再容斥;
总的-不合法的,总的:(2n-1)!
假设至少x对cp相邻的法案为f(x) = 2x * Cxn * (2n - 1 - x)! 表示为至少选x对情侣Cxn,每对相邻坐相对位置有2种,所以乘上2x ,然后把选出来的2*x个人组成x个个体去算圆排列相当于减去了x个人。
容斥:(2n-1)!-f(1)+f(2)-f(3)....f(n)
因为内存开俩倍老是MLE,所以就从后往前算
#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair typedef long long ll; const int M=30000005; const int N=M+5; const int inf=0x3f3f3f3f; const ll INF=1e18; const int mod=1e9+7; int fac[M],facinv[M]; int ksm(int x,int y){ int t=1; while(y){ if(y&1) t=(1ll*t*x)%mod; x=(1ll*x*x)%mod; y>>=1; } return t; } int inv(int x){ return ksm(x,mod-2)%mod; } int C(int x,int y){ if(y>x||x<0||y<0) return 0; if(y==0||x==y) return 1; return 1ll*fac[x]*1ll*facinv[y]%mod*1ll*facinv[x-y]%mod; } void init(int n){ fac[0]=1; for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod; int now=n; facinv[now]=ksm(fac[now],mod-2); for(int i=now-1;i>=0;i--) facinv[i]=1ll*facinv[i+1]*(i+1)%mod; } int main(){ int n; scanf("%d",&n); if(n==0||n==1) return printf("0"),0; init(n); int ans=0; for(int i = n,Pow = ksm(2,n),Fac = fac[n - 1]; i >= 0 ; Pow = 1ll * Pow * facinv[2] % mod,Fac = 1ll * Fac * (2 * n - i) % mod,i--) { if(i&1) ans = ((1ll * ans - (1ll * C(n,i) * Pow % mod * 1ll * Fac % mod)) % mod + mod) % mod; else ans = ((1ll * ans + (1ll * C(n,i) * Pow % mod * 1ll * Fac % mod)) % mod + mod) % mod; } printf("%d ",(ans%mod+mod)%mod); return 0; }