[BZOJ 3560] DZY Loves Math V

BZOJ传送门

Description

给定 n n n个正整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,求

img

的值(答案模 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 1n1051ai107。共 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}) ϕ(pi=1nbi,i[1,n],biai)
然而我们发现这样搞太麻烦了, 而每个数中对于这个质数取的方式又是相互独立的,且 ϕ ( p k ) = p k − 1 × ( p − 1 ) \phi(p^k)=p^{k-1}\times (p - 1) ϕ(pk)=pk1×(p1), 所以可以直接把全部选 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 pp1((i=1nj=0ajpj)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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值