首页 > 其他分享 >「解题报告」LOJ561 「LibreOJ Round #9」CommonAnts 的调和数

「解题报告」LOJ561 「LibreOJ Round #9」CommonAnts 的调和数

时间:2023-05-24 17:55:23浏览次数:39  
标签:10 frac LibreOJ int LOJ561 sum CommonAnts 1ll mul

模拟赛考的题,但是模拟赛没有打,哈哈,摆烂。

考场上想到大致做法了,没继续推,去打 GP of Tokyo 了。


首先发现操作都在查询前面,所以我们只需要预处理出答案即可。

我们先记 \(b_i\) 表示对 \(i\) 进行的操作的总和,那么容易写出 \(a_i\) 的式子:

\[a_i = \sum_{j | i} b_j \cdot \frac{i}{j} \]

然后考虑查询操作,也可以写出式子:

\[c_i = \sum_{i | j} a_j \cdot \frac{j}{i} \]

发现两个操作都是狄利克雷前缀和 / 后缀和的形式,于是直接暴力计算就能做到 \(O(n \log n) / O(n \log \log n)\)。

考虑题目中给出的限制:所有的操作下标的总质因子个数不超过 \(10\) 个。拿 \(\{2, 3, 5, 7, 11, 13, 17, 19, 23, 29\}\) 打个表,发现只出现这些数为质因子的不超过 \(10^9\) 的数仅有约 \(2 \times 10^5\) 个。这个性质是很优秀的,因为这告诉我们 \(a_i, c_i\) 中有用的数只有 \(2 \times 10^5\) 个。我们记这个数量为 \(V\)。

考虑直接把两个式子揉到一起:

\[\begin{aligned} c_i &= \sum_{i | j} \sum_{k | j} b_k \frac{j}{k} \frac{j}{i}\\ &= \frac{1}{i} \sum_{i | j} j^2 \sum_{k | j} \frac{b_k}{k}\\ \end{aligned} \]

看起来直接做就行了,但是发现问题出在枚举的倍数 \(j\) 上,这个数是可以达到 \(10^9\) 的量级的。但是观察后面的式子,我们枚举的是 \(j\) 的因子,而 \(b_k\) 仅有在 \(k\) 只存在前面的 \(10\) 个质因子的时候有值,这意味着 \(j\) 除了前面的 \(10\) 个质因子之外的因子是对答案不造成任何贡献的。而又因为 \(j^2\) 完全积性的性质,我们可以先枚举所有只存在 \(10\) 个质因子的数,然后再枚举所有不包含 \(10\) 个质因子的数,这样就能把 \(j\) 的贡献拆开了。

具体的,我们设 \(f(n) = \sum_{i = 1}^n i^2 [\gcd(i, p_1 p_2 \cdots p_{10}) = 1]\),\(g_i = \sum_{j | i} \frac{b_j}{j}\),\(S\) 为 \([1, n]\) 中仅存在 \(10\) 个质因子的数的集合,那么上式可以化为:

\[c_i = \frac{1}{i} \sum_{i | j, j \in S} f\left(\left\lfloor\frac{n}{j}\right\rfloor\right)j^2 g_j\\ \]

\(g_j\) 可以直接狄利克雷前缀和计算出来,外层也可以直接狄利克雷后置和计算,现在只需要计算出 \(f(n)\) 即可。注意到 \(f(n)\) 仅会取到 \(O(\sqrt{n})\) 个取值,所以考虑直接预处理出来所有值。

这部分好像做法很多,有一个很暴力的做法就是考虑容斥,\(2^{10}\) 枚举 \(i\) 一定出现过某些质因子,然后计算一下 \(i^2\) 的前缀和即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005, P = 998244353;
int n, m, q;
int p[MAXN], x[MAXN], k[MAXN];
vector<int> fac, s;
int qpow(int a, int b) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1ll * ans * a % P;
        a = 1ll * a * a % P;
        b >>= 1;
    }
    return ans;
}
const int INV6 = qpow(6, P - 2);
void dfs(int i, int x) {
    if (i == fac.size())
        s.push_back(x);
    else {
        for (long long k = 1; x * k <= n; k *= fac[i]) {
            dfs(i + 1, x * k);
        }
    }
}
unordered_map<int, int> a, b, c;
int f(int x) {
    x %= P;
    return 1ll * x * (x + 1) % P * (2 * x + 1) % P * INV6 % P;
}
int main() {
    scanf("%d%d%d", &n, &m, &q);
    auto factor = [&](int x) {
        for (int i : fac) {
            while (x % i == 0) x /= i;
        }
        if (x != 1) {
            for (int i = 2; i * i <= x; i++) {
                if (x % i == 0) {
                    fac.push_back(i);
                    while (x % i == 0) x /= i;
                }
            }
            if (x != 1) {
                fac.push_back(x);
            }
        }
    };
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &p[i], &x[i]);
        factor(p[i]);
    }
    for (int i = 1; i <= q; i++) {
        scanf("%d", &k[i]);
        factor(k[i]);
    }
    dfs(0, 1);
    for (int i = 1; i <= m; i++) {
        a[p[i]] = (a[p[i]] + x[i]) % P;
    }
    sort(fac.begin(), fac.end());
    sort(s.begin(), s.end());
    for (int x : s) {
        a[x] = 1ll * a[x] * qpow(x, P - 2) % P;
    }
    for (int pri : fac) {
        for (int x : s) if (1ll * x * pri <= n) {
            a[x * pri] = (a[x * pri] + a[x]) % P;
        }
    }
    for (int x : s) {
        int m = n / x;
        if (b.count(m)) continue;
        for (int t = 0; t < (1 << fac.size()); t++) {
            int mul = 1;
            for (int i = 0; i < fac.size(); i++) if (t >> i & 1) {
                if (1ll * mul * fac[i] > m) {
                    mul = 0;
                    break;
                }
                mul *= fac[i];
            }
            if (!mul) continue;
            b[m] = (b[m] + 1ll * ((__builtin_popcount(t) & 1) ? P - 1ll : 1ll) * mul % P * mul % P * f(m / mul)) % P;
        }
    }
    reverse(s.begin(), s.end());
    for (int x : s) {
        c[x] = 1ll * b[n / x] * x % P * x % P * a[x] % P;
    }
    for (int pri : fac) {
        for (int x : s) if (x % pri == 0) {
            c[x / pri] = (c[x / pri] + c[x]) % P;
        }
    }
    for (int x : s) {
        c[x] = 1ll * c[x] * qpow(x, P - 2) % P;
    }
    for (int i = 1; i <= q; i++) {
        printf("%d\n", c[k[i]]);
    }
    return 0;
}

标签:10,frac,LibreOJ,int,LOJ561,sum,CommonAnts,1ll,mul
From: https://www.cnblogs.com/apjifengc/p/17429103.html

相关文章

  • LibreOJ526. 「LibreOJ β Round #4」子集
    简要题意给出一个长度为\(n\)的序列\(a\)。你需要选出它的一个子集\(S\)。使其满足对于任意两个元素\(i,j\)。满足:\[\gcd(i,j)\cdot\gcd(i+1,j+1)\neq1\]输出满足条件的子集大小的最大值。\(1\leqn\leq500,1\leqa_i\leq10^{18}\)思路这道题比CodeforcesGym1......
  • LibreOJ L3735 「COCI 2015.11」 VUDU
    https://loj.ac/p/3735套路\(\times3\)。对于求平均数,直接对每一个\(a_i\getsa_i-P\),和\(\ge0\)的连续子序列就满足平均数\(>P\)。然后对其进行前缀和处理\(......
  • LibreOJ L2056 「TJOI / HEOI2016」序列
    https://loj.ac/p/2056CDQ优化DP模板?首先定义对于第\(x\)个数其可以变为的最小值为\(Min_x\),最大值为\(Max_x\),初始为\(M_x\)。因为最多只会变一次数,不难想到......
  • LibreOJ L6210 「美团 CodeM 决赛」tree
    链接难度:\(\texttt{?}\)有一颗\(n\)个点的树,每个点有权值\(x_i\),定义一条简单路径的权值为\(f(a_1\toa_2\to...\toa_k)=\frac{x_{a_1}\timesx_{a_2}\times...\t......
  • 图论专题 - LibreOJ
    第三部分图论第1章最小生成树#10064「一本通3.1例1」黑暗城堡#10065「一本通3.1例2」北极通讯网络#10066「一本通3.1练习1」新的开始#10067「一本通......
  • LibreOJ L2576 「TJOI2018」str
    链接难度:\(\texttt{?}\)有\(k\)组,每一组有\(a_i\)个字符串\(t_{k,a_i}\),询问分别在每一组选一个字符串拼成的字符串在文本串\(s\)出现的个数的所有情况的总和对......
  • LibreOJ #6042. 「雅礼集训 2017 Day7」跳蚤王国的宰相
    题意修改一条边意味着,删掉一条边,并加入一条新的边。给出一棵树,对于每个点,求出使它变成重心的最小修改边数。分析先找到重心,对于不是重心的一个点\(i\),有两种方法,一是......