著名夫妻圆排列问题

圆排列:

问题:有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\n",(ans%mod+mod)%mod);
    return 0;

    }
View Code

 

posted @ 2020-09-24 14:04  starve_to_death  阅读(395)  评论(0编辑  收藏  举报