【Kickstart】2018 Round H - Let Me Count The Ways

博客介绍了用容斥原理解决夫妇排列问题的解法。先假设对夫妇时,计算有队夫妇粘在一起的种类数,得出整个问题的种类数公式。接着说明用O(m + n)时间算出三部分值并存储,再用O(m)时间求和,还提到python会超时,需改成c++。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解法

用容斥原理做
假设P(n,i)表示n对夫妇时,有i队夫妇粘在一起的种类数:
我们选出i对夫妇,有C(m,i)种可能,把粘在一起的夫妇算做一个人,总共有2n-i个人全排列种排法,每个粘在一起的夫妇都有2种排法,所以总共有P(n,i)=2i(2n−i)!CmiP(n,i) = 2^i(2n-i)!C_m^iP(n,i)=2i(2ni)!Cmi种可能,整个问题的种类数就为:
S(n,m)=P(n,0)−P(n,1)+P(n,2)....=∑i=0m(−1)iP(n,i)=∑i=0m(−2)i(2n−i)!CmiS(n,m) = P(n,0)-P(n,1)+P(n,2)....\\=\sum_{i=0}^m(-1)^iP(n,i)\\=\sum_{i=0}^m(-2)^i(2n-i)!C_m^iS(n,m)=P(n,0)P(n,1)+P(n,2)....=i=0m(1)iP(n,i)=i=0m(2)i(2ni)!Cmi
O(m+n)O(m+n)O(m+n)时间算出这三部分的值,存起来,其中第一部分要变成(MOD−2)i(MOD-2)^i(MOD2)i,第三部分要变成Cmi=(m−i+1)iMOD−2Cmi−1C_m^i = (m-i+1)i^{MOD-2}C_m^{i-1}Cmi=(mi+1)iMOD2Cmi1
然后再用O(m)O(m)O(m)时间求和就行了
python会超时,改成c++就好了= =

#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <functional>

#define MAXN 100010
#define NINF -100000
#define INF 65536


using namespace std;

typedef long long lld;

int n,m;
const int MOD = 1000000007;
const int minus2 = MOD-2;
int A[MAXN],C[MAXN],B[MAXN<<1];
int maxn,maxm;

int POW(int i,int k) {
    int res = 1;
    int base = i;
    while (k>0) {
        if (k&1)
            res = (lld)res*base%MOD;
        base = (lld)base*base%MOD;
        k = k>>1;
    }
    return res;
}

int solve() {
    for(int i=maxm+1;i<=m;++i) {
        A[i] = (lld)A[i-1]*minus2%MOD;
    }
    for(int i=1;i<=m;++i) {
        C[i] = (lld)(m-i+1)*POW(i,minus2)%MOD*C[i-1]%MOD;
    }
    maxm = max(m,maxm);
    int UP = (n<<1);
    for(int i=(maxn<<1)+1;i<=UP;++i) {
        B[i] = (lld)B[i-1]*i%MOD;
    }
    maxn = max(n,maxn);
    int ans = B[n<<1];
    for(int i=1;i<=m;++i) {
        ans = (ans+(lld)A[i]*B[2*n-i]%MOD*C[i]%MOD)%MOD;
    }
    return ans;
}

int main() {
//    freopen("/Users/huangyuemei/CLionProjects/untitled/C-large-practice.in","r",stdin);
//    freopen("/Users/huangyuemei/CLionProjects/untitled/my.out","w",stdout);
    A[0] = C[0] = B[0] = 1;
    maxn = maxm = 0;
    int t;
    scanf("%d",&t);
    for(auto round=1;round<=t;++round) {
        scanf("%d%d",&n,&m);
        printf("Case #%d: %d\n",round,solve());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值