最后的晚餐(dinner)


 

 

题目描述

    **YZ(已被和谐)的食堂实在是太挤辣!所以Apojacsleam现在想邀请他的一些好友去校外吃一顿饭,并在某酒店包下了一桌饭。

    当Apojacsleam和他的同学们来到酒店之后,他才发现了这些同学们其实是N对cp,由于要保护广大单身狗的弱小心灵(FF!),所以他不想让任意一对情侣相邻。

    说明:

        ·酒店的桌子是恰好有2N个位置的圆桌。

        ·客人恰好是N对cp,也就是说,圆桌上没有空位。

        ·桌子的每一个位置是一样的,也就是说,如果两种方案可以通过旋转得到,那么这就可以视为相等的。

    现在,你需要求出,将任意一对情侣不相邻的方案数。

方法1:

首先这是一个环,所以1234与2341是同样的方案,那么从n对情侣中先把男生挑出来共n个人先去坐位子,共(n-1)!个方案。

接下来就是把剩下n个人插入进去了。
定义dp[i]代表安排完前i个人的时候的方案数。

1、dp[i-1] * (n+i-3)

上面的式子是直接插入方式,安排第i个人的时候,一共有n+i-1个人,那么就有n-i+1个位置可以选,但是又不能和自己情侣相邻,减掉她左右的两个位置即可。

2、dp[i-2] * (i-1) * 2

这个式子是间接插入,就是说现在在安排第i个人,但是第i-1个人我暂时安排他和他情侣在一起,把第i个人插入他们两者之间即可最后正常。


所以通过观察间接插入和直接插入是完全独立的两个方案,不会出现重复,注意特判一下n=1的情况即可。
所以最终答案就是 dp[i] = dp[i-1] * (n+i-3) + dp[i-2] * (i-1) * 2
最终把分配男生的(n-1)!的方案乘进去即可。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=30000000;
const long long mod=1000000007;
long long f[N+5];
int main(){
    int n;
    scanf("%d",&n);
    if(n==1){
        printf("0");
        return 0;
    }
    f[0]=1;
    f[1]=n-2;
    for(int i=2;i<=n;i++)
        f[i]=(f[i-1]*(n+i-3)%mod+f[i-2]*(i-1)%mod*2%mod)%mod;
    for(int i=2;i<n;i++)
        f[n]=f[n]*i%mod;
    printf("%lld",f[n]);
    return 0;
}

方法2:

朴素容斥

 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N=3e7+5,mod=1000000007,INV=500000004;
LL fac,tfac,inv[N],po=1;
LL ksm(LL a,LL n)
{
    LL res=1;
    while(n)
    {
        if(n&1)res=res*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return res;
}
void init(LL n)
{
    tfac=1;
    for(LL i=1;i<=n;i++)tfac=tfac*i%mod,po=po*2%mod;
    fac=tfac*ksm(n,mod-2)%mod;
    inv[n]=ksm(tfac,mod-2);
    for(LL i=n-1;~i;i--)inv[i]=inv[i+1]*(i+1)%mod;
    //inv[1]=inv[0]=1;
}
LL C(LL a,LL b)
{
    //if(a<b)return 0;
    return tfac%mod*inv[b]%mod*inv[a-b]%mod;
}
int main()
{
    LL n;
    scanf("%lld",&n);
    if(n<=1)
    {
    	puts("0");
    	return 0;
	}
    init(n);
    LL res=0;
    for(int i=n;~i;i--)
    {
    	//cout<<ksm(2,i)<<endl;
        if(i&1)res=((res-fac*C(n,i)%mod*po%mod)%mod+mod)%mod;
        else res=(res+fac*C(n,i)%mod*po%mod+mod)%mod;
        fac=fac*(2*n-i)%mod;
        po=po*INV%mod;
    }
    printf("%lld",res);
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值