Codeforces Round #678 (Div. 2)

这篇博客介绍了数学竞赛中的一道题目,涉及集合子集的求和与最大公约数问题。通过容斥原理与高斯取模计算解答,解释了如何忽略条件3并计算总和,以及如何使用容斥思想处理最大公约数为1的条件。文章给出了详细的解题思路和代码实现,展示了如何在O(n log n)的时间复杂度内求解问题。

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

F. Sum Over Subsets

题意:
给定一个集合S,求所有A,B集合,满足以下条件:
1.B⊂AB \subset ABA
2.∣B∣=∣A∣−1|B|=|A|-1B=A1
3.gcd(A)=1gcd(A)=1gcd(A)=1
对所有的A,B集合求和∑x∈Ax⋅∑x∈Bx\sum_{x\in A}x \cdot \sum_{x\in B}xxAxxBx
结果对998244353998244353998244353取余
题解:
忽略条件3,计算此时的和,假设∣S∣=k|S|=kS=k

  1. ai⋅aia_i \cdot a_iaiai被计算2k−2(k−1)2^{k-2}(k-1)2k2(k1)次,其中k−1k-1k1表示AAABBB多的那个元素方案,2k−22^{k-2}2k2表示剩下元素的方案。
  2. ai⋅aja_i \cdot a_jaiaj被计算2k−3(k−2)+2k−22^{k-3}(k-2)+2^{k-2}2k3(k2)+2k2次,前半部分表示ai∈Aa_i \in AaiAai∈Ba_i \in BaiB时的方案数,后半部分表示ai∈Aa_i \in AaiA ai∉Ba_i \notin Bai/B的方案。

条件3可以利用容斥的思想。设f[i]f[i]f[i]表示gcd(S)=igcd(S)=igcd(S)=i的答案,g[i]g[i]g[i]表示i∣gcd(S)i|gcd(S)igcd(S)的答案。现在是已知gggfff
那么f[i]=g[i]−∑i∣j,i!=jf[j]f[i]=g[i]-\sum_{i|j,i!=j}f[j]f[i]=g[i]ij,i!=jf[j]

#include<bits/stdc++.h>

using namespace std;
#define LL long long
const int mod = 998244353;
const int N = 1e5 + 5;
int a[N], freq[N], sum[N], sumsquare[N], f[N], g[N];
LL num[N];

int qpow(int x, LL y) {
    int ans = 1;
    while (y) {
        if (y & 1) {
            ans = 1ll * ans * x % mod;
        }
        x = 1ll * x * x % mod;
        y /= 2;
    }
    return ans;
}

int main() {
    int m;
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d %d", &a[i], &freq[i]);
        num[a[i]] = num[a[i]] + freq[i];
        sum[a[i]] = (sum[a[i]] + 1ll * a[i] * freq[i] % mod) % mod;
        sumsquare[a[i]] = (sumsquare[a[i]] + 1ll * a[i] * a[i] % mod * freq[i] % mod) % mod;
    }
    for (int i = 1; i < N; i++) {
        for (int j = 2 * i; j < N; j += i) {
            num[i] = num[i] + num[j];
            sum[i] = (sum[i] + sum[j]) % mod;
            sumsquare[i] = (sumsquare[i] + sumsquare[j]) % mod;
        }
    }
    for (int i = 1; i < N; i++) {
        LL k = num[i];
        if (k >= 2) {
            g[i] = (g[i] + 1ll * sumsquare[i] * qpow(2, k - 2) % mod * ((k - 1) % mod) % mod) % mod;
            int temp = 0;
            temp = (temp + 1ll * qpow(2, k - 3) * ((k - 2) % mod) % mod) % mod;
            temp = (temp + 1ll * qpow(2, k - 2) % mod) % mod;
            g[i] = (g[i] + 1ll * (1ll * sum[i] * sum[i] % mod - sumsquare[i] + mod) % mod * temp % mod) % mod;
        }
    }

    for (int i = N - 1; i >= 1; i--) {
        f[i] = g[i];
        for (int j = 2 * i; j < N; j += i) {
            f[i] = (f[i] - f[j] + mod) % mod;
        }
    }
    printf("%d\n", f[1]);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值