The 2021 CCPC Weihai Onsite M

Link:https://codeforces.com/gym/103428/problem/M

知识点:组合数学,容斥

简述

给定整数 n,m,k,求满足如下条件的 01 串的数量:

  • 长度为 n
  • 1 的个数为 m
  • 最长的连续全 1 段的长度为 k

0n,m,k105
2S,256MB。

分析

首先把限制 k 搞掉,记 f(x) 为仅允许有长度不大于 x 的全 1 段的方案数,则限定最长段为 k 的方案数即为 f(k)f(k1)

然后考虑如何求 f(k)。限定串中有 m 个 1,即有 nm 个 0,则构造字符串等价于在 nm 个 0 之间和两端共 nm+1 个空中填入总共 m 个 1,每个空可填入 0k 个 1。这是个经典问题,参考 hdu6397,考虑容斥消去填入上限的限制。

g(i) 表示总共填入了 m 个 1 且没有填入上限,有至少 i 个空至少填入了 k+1 的方案数:

  • 显然有 0imin(nm+1,m(k+1))
  • 对于 i=0,即每个空没有填数上限,则直接插板法,方案数为 ((nm+1)+m1nm1+1)=(nnm)
  • 对于 i>0,考虑先选出 i 个空为它们预分配 k+1,然后转化为了 i=0 的情况,方案数为 (nm+1i)×(n(k+1)×inm)

则有 f(k)=i(1)i×g(i),注意特判 f(1)=0

预处理下阶乘和逆元,总时间复杂度 O(n) 级别。

代码

复制复制
//知识点:组合数学,容斥
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
const LL p = 998244353;
//=============================================================
int n, m, k;
LL inv[kN], fac[kN], ifac[kN];
//=============================================================
LL C(LL n_, LL m_) {
if (m_ > n_) return 0;
return fac[n_] * ifac[m_] % p * ifac[n_ - m_] % p;
}
void Init() {
inv[1] = fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for (int i = 2; i < kN; ++ i) {
inv[i]= 1ll * (p - p / i + p) % p * inv[p % i] % p;
fac[i] = fac[i - 1] * i % p;
ifac[i] = ifac[i - 1] * inv[i] % p;
}
}
LL f(LL k_) {
if (k_ == -1) return 0;
LL ans = 0, f = 1;
for (int i = 0; i <= std::min(n - m + 1ll, m / (k_ + 1)); ++ i, f = -f) {
LL d1 = C(n - m + 1, i), d2 = C(n - (k_ + 1) * i, n - m);
ans = (ans + f * d1 * d2 % p + p) % p;
}
return ans;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
Init();
std::cin >> n >> m >> k;
std::cout << (f(k) - f(k - 1) + p) % p << "\n";
return 0;
}
posted @   Luckyblock  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示