[BZOJ3560]DZY Loves Math V 题解

[BZOJ3560]DZY Loves Math V 题解

题目地址:BZOJ:Problem 3560. — DZY Loves Math V

题目描述

给定n个正整数a1,a2,…,an,求 \displaystyle \sum_{i_1|a_1} \sum_{i_2|a_2} \cdots \sum_{i_n|a_n} \varphi(i_1 i_2 \cdots i_n)的值(答案模10^9+7)。

输入输出格式

输入格式:
第一行一个正整数n。
接下来n行,每行一个正整数,分别为a1,a2,…,an。

输出格式:
仅一行答案。

输入输出样例

输入样例#1:

3
6
10
15

输出样例#1:

1595

说明

1<=n<=10^5,1<=ai<=10^7。共3组数据。

题解

首先考虑到\varphi(x)是积性函数,因此我们可以对ai中的每一个质因数讨论,如果某个因数p在ai中的个数是bi,则计算出每个因数的
\sum_{i_1=0}^{b_1} \sum_{i_2=0}^{b_2} \cdots \sum_{i_n=0}^{b_n} \varphi(p^{i_1+i_2+\cdots+i_n})
并乘起来就得到了最终结果。然而强行计算这个值复杂度不可接受,我们需要给出一种优化算法,要对这个式子化简。
我们知道\varphi(x)有一种求法,如果x的所有质因数分别为pi,则
\varphi(x) = x(1 - \frac{1}{p_1})(1 - \frac{1}{p_2}) \cdots (1 - \frac{1}{p_n})
那么我们观察每个因数的结果式,显然1-分数只有一项即 (1-\frac{1}{p}) ,前面的x可以求和,即化简成下面这种形式
\left[ \left( \sum_{i_1=0}^{b_1} \sum_{i_2=0}^{b_2} \cdots \sum_{i_n=0}^{b_n} p^{i_1+i_2+\cdots+i_n} \right) - 1 \right] (1-\frac{1}{p}) + 1
括号里的-1是减去p_0的情况,这种情况我们在外面计算,即\varphi(1) = 1。前面的一坨东西仍然会使复杂度变大,但是这种形式已经好优化了
\left[ \left( \prod_{i=1}^n \sum_{j=0}^{b_i} p^j \right) - 1 \right] (1-\frac{1}{p}) + 1
这样我们只需要对p的乘方求前缀和,就可以实现O(n)计算每个因数了。总复杂度O(n \sqrt{n})

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>

#include <algorithm>
#include <vector>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for (; !isdigit(c); c = fgc()) if (c == '-') neg = -1;
    for (; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 1000005, MO = 1e9 + 7;

inline LL fpow(LL x, LL k) {
    LL res = 1;
    for(; k; k >>= 1) {
        if(k & 1) res = res * x % MO;
        x = x * x % MO;
    }
    return res;
}

int n, a[100005];
typedef std::pair<int, int> PII;
std::vector<PII> fact;

inline void calfact(int x) {
    for(int i = 2; i * i <= x; i++) {
        if(x % i) continue;
        int cnt = 0;
        while(x % i == 0) {
            x /= i; cnt++;
        }
        fact.push_back(PII(i, cnt));
    }
    if(x > 1) fact.push_back(PII(x, 1));
}

LL sum[50];

inline LL calc(int l, int r) {
    sum[0] = 1; int p = fact[l].first;
    for(int i = 1; i <= fact[r].second; i++) {
        sum[i] = sum[i - 1] * p % MO;
    }
    for(int i = 1; i <= fact[r].second; i++) {
        sum[i] = (sum[i - 1] + sum[i]) % MO;
    }
    LL res = 1;
    for(int i = l; i <= r; i++) {
        res = res * sum[fact[i].second] % MO;
    }
    res--; 
    res = res * (p - 1) % MO; res = res * fpow(p, MO - 2) % MO;
    res++; res %= MO;
    return res;
}

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        a[i] = readint(); calfact(a[i]);
    }
    std::sort(fact.begin(), fact.end());
    LL ans = 1; int lst = -1;
    for(int i = 0; i < fact.size(); i++) {
        if(i == fact.size() - 1 || fact[i].first != fact[i + 1].first) {
            ans = ans * calc(lst + 1, i) % MO; lst = i;
        }
    }
    printf("%lld", ans);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据