CCPC 2024 济南站

F. The Hermit

题目描述

知名丹药师戌狗在炼丹时发现精准剔除杂质能够提升丹药的灵韵。在日复一日地炼丹中,他发现了杂质的性质竟然与数学问题有千丝万缕的关系。由于你道行尚浅,戌狗决定以最直白的方式告诉你他需要解决的数学问题,而不是玄之又玄的炼丹问题。

给定两个正整数 nm,对 {1,2,,m} 的所有大小为 n 的子集,计算以下问题的答案值求和,并对 998244353 取模:

  • n 个数的集合里,你可以删掉若干个数,使得集合的最小值不等于集合的最大公约数,问满足条件的方案里没有被删的数最多有多少个。如果无解,答案定义为 0

集合的最大公约数定义为所有元素的共同约数中的最大值。例如,集合 {6,9,15} 的最大公约数是 3

输入描述

输入一行,包含两个整数 n,m (1nm105)

输出描述

输出一个整数表示答案,对 998244353 取模。

解题思路

对于 {1,,m} 的所有大小为 n 的子集进行操作,可以考虑每个元素被删了多少次,从nCmn减去得到答案,而对于一个元素 x ,如果它会被删除,则说明

  • 集合前一部分满足 {a,ab,,abcx} 形式,即后一个数是前一个数的倍数(称这个部分为倍数链,它的长度一定不会超过 log2x+1
  • 集合后一部分满足 {x,2x,,nx} 形式,即 x 后面的数都是 x 的倍数

前一部分可以直接枚举倍数求得 fi,x ,表示倍数链长度为 i,且当前的数是 x 的方案数
后一部分可以直接在倍数里任选

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int N = 1e5 + 10;
const int MOD = 998244353;

std::vector<i64> fac(N + 1, 1), invfac(N + 1, 1);

i64 ksm(i64 a, i64 n, i64 MOD) {
    i64 ans = 1;
    a = (a % MOD + MOD) % MOD;
    while (n) {
        if (n & 1) {
            ans = (a * ans) % MOD;
        }
        a = (a * a) % MOD;
        n >>= 1;
    }
    return ans;
}

void init(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
    }
    invfac[n] = ksm(fac[n], MOD - 2, MOD);
    for (int i = n - 1; i >= 0; i--) {
        invfac[i] = invfac[i + 1] * (i + 1) % MOD;
    }
}

i64 C(int n, int m) {  // 组合数
    if (m > n || m < 0) {
        return 0;
    }
    return fac[n] * invfac[m] % MOD * invfac[n - m] % MOD;
}

i64 A(int n, int m) {  // 排列数
    if (m > n || m < 0) {
        return 0;
    }
    return fac[n] * invfac[n - m] % MOD;
}

i64 catalan(int n) {  // 卡特兰数
    if (n < 0) {
        return 0;
    }
    return C(2 * n, n) * ksm(n + 1, MOD - 2, MOD) % MOD;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    init(N);
    int m, n;
    std::cin >> m >> n;

    std::unordered_map<int, std::unordered_map<int, int>> f;
    for (int x = 1; x <= m; x++) {
        f[1][x] = 1;
    }

    int ans = n * C(m, n) % MOD;
    for (int i = 1; i <= n; i++) {  // 枚举长度
        for (auto [x, y] : f[i]) {
            ans = ans - f[i][x] * C(m / x - 1, n - i) % MOD;  // 直接枚举倍数
            ans %= MOD;
            if (ans < 0) {
                ans += MOD;
            }
            for (int j = 2; j * x <= m && i + 1 <= n; j++) {  // 更新倍数链
                f[i + 1][j * x] = (f[i + 1][j * x] + f[i][x]) % MOD;
            }
        }
    }
    std::cout << ans << '\n';
}
posted @   udiandianis  阅读(191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
  1. 1 山海不可平 CMJ
山海不可平 - CMJ
00:00 / 00:00
An audio error has occurred.

纯音乐,请欣赏

点击右上角即可分享
微信分享提示