BZOJ传送门
Description
给定 n n n个正整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an,求
的值(答案模 1 0 9 + 7 10^9+7 109+7)。
Input
第一行一个正整数 n n n。
接下来 n n n行,每行一个正整数,分别为 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
Output
仅一行答案。
Sample Input
3
6
10
15
Sample Output
1595
HINT
1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 7 1\le n\le 10^5,1\le a_i\le 10^7 1≤n≤105,1≤ai≤107。共 3 3 3组数据。
解题分析
因为 ϕ ( i ) \phi(i) ϕ(i)为积性函数, 所以很容易想到分开讨论每一个质因数的贡献。
那么对于一个质数
p
p
p, 设其在
n
n
n个数中分别可以分离出
p
a
1
,
p
a
2
,
.
.
.
,
p
a
n
p^{a_1},p^{a_2},...,p^{a_n}
pa1,pa2,...,pan, 那么其贡献为
∑
ϕ
(
p
∑
i
=
1
n
b
i
,
∀
i
∈
[
1
,
n
]
,
b
i
≤
a
i
)
\sum\phi(p^{\sum_{i=1}^{n}b_i,\forall i\in[1,n],b_i\le a_i})
∑ϕ(p∑i=1nbi,∀i∈[1,n],bi≤ai)
然而我们发现这样搞太麻烦了, 而每个数中对于这个质数取的方式又是相互独立的,且
ϕ
(
p
k
)
=
p
k
−
1
×
(
p
−
1
)
\phi(p^k)=p^{k-1}\times (p - 1)
ϕ(pk)=pk−1×(p−1), 所以可以直接把全部选
0
0
0的情况提出来单独考虑,这么写:
p
−
1
p
(
(
∏
i
=
1
n
∑
j
=
0
a
j
p
j
)
−
1
)
+
1
\frac{p-1}{p}((\prod_{i=1}^{n}\sum_{j=0}^{a_j}p^j) -1)+1
pp−1((i=1∏nj=0∑ajpj)−1)+1
那么我们把
1
0
7
10^7
107之内的质数全部搞出来, 然后大力对每个
a
i
a_i
ai分解质因数, 像上面那样算出贡献, 最后把每个质数的贡献乘起来就好了。
复杂度大概是 O ( m a x ( a i ) + n m a x ( a i ) l n ( n ) ) O(max(a_i)+\frac{n\sqrt {max(a_i)}}{ln(n)}) O(max(ai)+ln(n)nmax(ai))的?
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 10000005
#define MOD 1000000007ll
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int pcnt, n;
int pri[MX];
bool npr[MX];
ll tot[MX], ans = 1;
void get()
{
R int i, j, tar;
for (i = 2; i <= 1e7; ++i)
{
if (!npr[i]) pri[++pcnt] = i;
for (j = 1; j <= pcnt; ++j)
{
tar = i * pri[j];
if (tar > 1e7) break;
npr[tar] = true;
if (!(i % pri[j])) break;
}
}
}
IN ll fpow(ll base, R int tim)
{
ll ret = 1;
W (tim)
{
if (tim & 1) ret = ret * base % MOD;
base = base * base % MOD, tim >>= 1;
}
return ret;
}
IN void work(R int val)
{
int bd = std::sqrt(val);
for (R int i = 1; pri[i] <= bd; ++i)
{
if (!(val % pri[i]))
{
ll res = 1, mul = pri[i];
W (!(val % pri[i])) res += mul, mul *= pri[i], val /= pri[i];
(tot[pri[i]] *= res) %= MOD;
if (!npr[val]) break;
}
}
if (val != 1) (tot[val] *= (val + 1)) %= MOD;
}
int main(void)
{
in(n); get(); int foo;
for (R int i = 1; i <= pcnt; ++i) tot[pri[i]] = 1;
for (R int i = 1; i <= n; ++i) in(foo), work(foo);
for (R int i = 1; i <= 1e7; ++i)
if (!npr[i]) tot[i] = (tot[i] - 1) * (i - 1) % MOD * fpow(i, MOD - 2) % MOD, ans *= (tot[i] + 1), ans %= MOD;
printf("%lld", ans);
}