【YBTOJ高效进阶 21190】欧拉函数(数学)
欧拉函数
题目链接:YBTOJ高效进阶 21190
题目大意
给你一些数 a1~an,然后要你求:
在每个数中选一个因子,它们的积的欧拉函数的结果的和。
思路
由于是欧拉函数,我们通过线性筛求它的方法,自然想到把每个质数分开来解决。
那就变成了你每个数可以贡献 \(0\sim k_{p,i}\)(\(k_{p,i}\) 是 \(p\) 在 \(i\) 质因数分解的时候出现的次数)
那你就考虑 DP,要么是这次是第一次贡献,要么是这一次不是。
那你是第一次贡献就是直接加上(记得要把 \(1\sim k_{p,i}\) 的贡献都加上)
然后不是第一次的话就是之前的答案乘上 \(0\sim k_{p,i}\) 的贡献。
然后记得要加上什么都不选的情况,然后每个质数的结果就可以了。
代码
#include
#include
#define ll long long
#define mo 1000000007
using namespace std;
int n, a[100001];
int prime[1000001];
int np[10000001], pl[10000001];
vector nm[1000001];
ll ans, f[2000001];
ll slove(int now) {//分别处理每个质数
for (int i = 0; i < nm[now].size(); i++) {
f[i + 1] = 0;
ll noww = 0, di = 1;
for (int j = 0; j < nm[now][i]; j++) {
noww = (noww + di) % mo;
di = di * prime[now] % mo;
}
f[i + 1] = noww * (prime[now] - 1) % mo;//这次有新的
noww = (noww + di) % mo;
f[i + 1] = (f[i + 1] + f[i] * noww % mo) % mo;//之前就有加上的
}
return (f[nm[now].size()] + 1) % mo;//加上什么都不选的
}
int main() {
// freopen("varphi.in", "r", stdin);
// freopen("varphi.out", "w", stdout);
for (int i = 2; i <= 10000000; i++) {
if (!np[i]) {
np[i] = i; prime[++prime[0]] = i;
pl[i] = prime[0];
}
for (int j = 1; j <= prime[0] && i * prime[j] <= 10000000; j++) {
np[i * prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
int noww = a[i];
while (noww != 1) {//分解一下质因数
int now = np[noww], ti = 0;
while (np[noww] == now) {
noww /= now; ti++;
}
nm[pl[now]].push_back(ti);
}
}
ans = 1;
for (int i = 1; i <= prime[0]; i++) {
ans = ans * slove(i) % mo;
}
printf("%lld", ans);
return 0;
}