[luogu4931]情侣?给我烧了!

本文深入探讨了在特定条件下计算情侣和谐排列方案数的算法。通过详细的数学推导,介绍了如何利用容斥原理和组合数学解决复杂的问题。文章提供了一种有效的方法来计算n对情侣中有k对和谐的方案数,并附带了完整的代码实现。

1464677-20190313215848023-1264397584.png


题解

\(i\)对情侣全都不和谐那里推不出来只好写了一个暴力容斥然后大力卡常卡过去了==
容斥太过暴力,还是说正解吧
可以考虑直接计算\(n\)对情侣有\(k\)对和谐的方案数
\(g[i]\)表示\(i\)对情侣\(2*i\)个座位,这\(i\)对情侣全都不和谐的方案数
那么答案显然就是\(C(n,k)*C(n,k)*fac[k]*2^k*g[n-k]\)
那么主要问题就是这个\(g[]\)怎么求
我们可以考虑像错排那样来推一下这个东西:
考虑有\(n\)对情侣:首先随便选一个坐在第一排的第一个位置,然后再选不是ta的情侣的坐在第一排的第二个位置,方案数是\((n*2)*(n*2-2)\)
那么考虑坐第一排的那两对的情侣,如果那两个也坐在同一排,方案数就是\((n-1)*g[n-2]\)
如果那两人不坐在同一排,那么可以把他们当做一对情侣,那么方案数就是\(g[n-1]\)
所以\(g[n]=(n*2)*(n*2-2)(g[n-1] +(n-1)*g[n-2])\)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 5000005 ;
const int mod = 998244353 ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n , k , f[M] , fac[M] , inv[M] , finv[M] , pw2[M] ;
inline int C(int n , int m) {
    return 1LL * fac[n] * finv[m] % mod * finv[n - m] % mod ;
}
inline int Calc(int n) {
    return (1LL * n * 2 * (n * 2 - 2) % mod * (n - 1) * 2 % mod * f[n - 2] + 1LL * n * 2 * (n * 2 - 2) % mod * f[n - 1] % mod ) % mod ;
}
int main() {
    fac[0] = 1 ; 
    for(int i = 1 ; i <= 5000000 ; i ++) fac[i] = 1LL * fac[i - 1] * i % mod ;
    inv[1] = 1 ;
    for(int i = 2 ; i <= 5000000 ; i ++) inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod ;
    finv[0] = 1 ;
    for(int i = 1 ; i <= 5000000 ; i ++) finv[i] = 1LL * finv[i - 1] * inv[i] % mod ;
    pw2[0] = 1 ;
    for(int i = 1 ; i <= 5000000 ; i ++) pw2[i] = pw2[i - 1] * 2 % mod ;
    f[0] = 1 ; f[1] = 0 ;
    for(int i = 2 ; i <= 5000000 ; i ++) f[i] = Calc(i) ;
    int Case = read() ;
    while(Case --) {
        n = read() ; k = read() ;
        printf("%d\n", (1LL * C(n , k) * C(n , k) % mod * fac[k] % mod * pw2[k] % mod * f[n - k] % mod + mod) % mod) ;
    }
    return 0 ;
}

转载于:https://www.cnblogs.com/beretty/p/10526942.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值