2021牛客寒假算法基础集训营5 B. 比武招亲(上)

博客主要探讨了一道数学与算法题目,涉及序列构造和贡献值计算。给定n和m,需要计算所有不同排序序列的贡献和,贡献值定义为序列最大值与最小值之差。解决方案通过非降序序列枚举差值,利用组合数学公式C(d+m-2, m-2)计算方案数,并最终求和得出答案。代码中实现了这一算法,使用了组合数的预处理和快速幂运算。
摘要由CSDN通过智能技术生成

链接

https://ac.nowcoder.com/acm/contest/9985/B

题意

给定 n , m n,m n,m,定义一种序列,构造方法如下:

  1. [ 1 , n ] [1,n] [1,n] 中任意选择 m m m 次,得到了 m m m 个整数(显然数字可能相同);
  2. 将选出的 m m m 个数字排序之后得到一个序列 { a 1 , a 2 , . . . , a m } \{ a_{1},a_{2},...,a_{m} \} {a1,a2,...,am}

定义一个序列的贡献为 max ⁡ { a 1 , a 2 , . . . , a m } − min ⁡ { a 1 , a 2 , . . . , a m } \max\{ a_{1},a_{2},...,a_{m} \}-\min\{ a_{1},a_{2},...,a_{m} \} max{a1,a2,...,am}min{a1,a2,...,am},求所有不同的序列的贡献和。

思路

本题顺序不同元素相同的序列是等价的,所以直接考虑非降序列。

枚举差值 d d d,最小数有 n − d n-d nd 种取法,考虑查分序列。

我们要求的是从 0 ∼ d 0\sim d 0d 中选出 m − 1 m-1 m1个数(可相同),使差分序列前缀和等于 d d d 的方案数 ,

即将 d d d 个相同的球放入 m − 1 m-1 m1 个不同的盒子,并允许空盒子的方案数 ,

等价于将 d + m − 1 d+m-1 d+m1 个相同的球放入 m − 1 m-1 m1 个不同的盒子,不允许空盒子的方案数 ,

所以 f ( d , m − 1 ) = ( d + m − 2 m − 2 ) f(d,m-1)=\binom{d+m-2}{m-2} f(d,m1)=(m2d+m2) r e s = ∑ d = 0 n − 1 d ∗ ( n − d ) ∗ f ( d , m − 1 ) res=\sum_{d=0}^{n-1}{d*(n-d)*f(d,m-1)} res=d=0n1d(nd)f(d,m1)

代码

#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const LL MOD=998244353;
const int N=1e6+5;
LL fac[N],invfac[N];
LL qpow(LL a,LL b) {
    LL ret=1;
    while(b) {
        if(b&1) ret=ret*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return ret;
}
void init_invfac(int n) {
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
    invfac[n]=qpow(fac[n],MOD-2);
    for(int i=n-1;~i;i--) invfac[i]=invfac[i+1]*(i+1)%MOD;
}
LL C(int a,int b) {
    return fac[a]*invfac[b]%MOD*invfac[a-b]%MOD;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,m;
    cin>>n>>m;
    init_invfac(n+m);
    LL res=0;
    for(int i=0;i<n;i++) res=(res+(n-i)*i%MOD*C(i+m-2,m-2)%MOD)%MOD;
    cout<<res<<'\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值