CF839D Winter is here 题解

说两个做法。

共同思路

设 $f_i$ 为 $\gcd=i$ 的子序列长度和。$f_i$ 不太好算,设 $g_i$ 为 $i|\gcd$ 的子序列长度和,即 $g_x=\sum\limits_{x|i}f_i$。

考虑怎样求 $g_i$。设 $c_i$ 为 $i$ 的出现次数,$s_i$ 为 $i$ 的倍数的出现次数,即 $s_x=\sum\limits_{x|i}c_i$。

容易发现 $g_i$ 为在 $s_i$ 个数中选出的子序列长度和,$s_i$ 个数中,每个数被 $2^{s_i-1}$ 个子序列包含,则 $g_i=2^{s_i-1}s_i$。

最后根据 $g_i$ 推出 $f_i$ 即可。注意到复杂度瓶颈在于求 $f_i,s_i$。

Sol. 1

考虑暴力求 $f_i,s_i$。复杂度是调和级数的,$O(n\log n)$ 可以过掉。

#include <cstdio>
#include <algorithm>
#define M 1000000007
using namespace std;
int n, m, c[1000050];long long q, p[200050], f[1000050];
int main()
{
    scanf("%d", &n);for(int i = p[0] = 1, x;i <= n;++i)
    scanf("%d", &x), ++c[x], m = max(m, x), p[i] = (p[i - 1] << 1) % M;
    for(int i = m, s;i > 1;--i)
    {
        s = 0;for(int j = i;j <= m;j += i) s += c[j];if(s)
        {f[i] = s * p[s - 1] % M;for(int j = i << 1;j <= m;
        j += i) (f[i] += M - f[j]) %= M;(q += i * f[i]) %= M;}
    }
    return printf("%lld", q), 0;
}

Sol. 2

注意到 $g_x=\sum\limits_{x|i}f_i,s_x=\sum\limits_{x|i}c_i$ 都是 Dirichlet 后缀和的形式,可以 $O(n\log\log n)$ 解决。

#include <cstdio>
#include <algorithm>
#define M 1000000007
using namespace std;
int n, m, d, a[1000050], c[1000050];long long q, p[200050], f[1000050];bool b[1000050];
int main()
{
    scanf("%d", &n);for(int i = p[0] = 1, x;i <= n;++i)
    scanf("%d", &x), ++c[x], m = max(m, x), p[i] = (p[i - 1] << 1) % M;
    for(int i = 2;i <= m;++i) {if(!b[i]) a[++d] = i;
    for(int j = 1;i * a[j] <= m;++j) {b[i * a[j]] = 1;if(!(i % a[j])) break;}}
    for(int i = 1;i <= d;++i) for(int j = m / a[i];j > 1;--j) c[j] += c[j * a[i]];
    for(int i = m;i > 1;--i) if(c[i]) f[i] = c[i] * p[c[i] - 1] % M;
    for(int i = 1;i <= d;++i) for(int j = 2;j * a[i] <= m;++j) (f[j] += M - f[j * a[i]]) %= M;
    for(int i = 2;i <= m;++i) (q += i * f[i]) %= M;return printf("%lld", q), 0;
}
posted @ 2022-08-27 08:43  Jijidawang  阅读(10)  评论(0)    收藏  举报  来源