目录
一.题目
题目描述
经过精灵族全力抵挡,精灵终于坚持到了联络系统的重建,于是精灵向人类求助,大魔法师伊扎洛决定弓}用博士的最新科技来抗敌。
伊扎洛:“博士,还没好吗?”
博士:“只差一步了!只需要在正确的位置装上弹药就可以了!”
博士的最新科技是全新的炸弹,但是现在还需要一步装弹药的操作。博士的炸弹有N!个位置可以装弹药(>.<),但是只有在正确的位置装上弹药才能启动,博士将装弹药的位置编号为1到N!,一个位置i需要装弹药,当且仅当gcd(i, N!) ≠ 1且N! mod i ≠ 0,现在博士不要求你求出所有装弹药位置,只需要你求出满足要求的位置的数量就可以了。
输入
一个数N
输出
表示满足要求的位置数量,答案对mod1000000007输出
样例输入
4
样例输出
9
提示
【数据范围】
N <= 1000000
二.题解
这道题其实已经很明显了,大家可以反着想,直接根据条件得出计算的公式:
从这个公式:gcd(i, N!) ≠ 1,我们可以得出,i和N!并不互质,所以减去与N!互质,且比N!小的数,就是欧拉函数。(命为x)
从这个公式:N! mod i ≠ 0,我们可以得出,i不是N!的因数,所以减去N!的所有因数。(命为y)
最后还要加一个1,因为1被减了两次。
所以ans=N!-x-y+1
令:(分解质因数legendre)
欧拉函数的求法:①
②
因数个数的求法:
三.代码
//注意多开long long
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define mod 1000000007
#define M 1000005
#define LL long long
LL n_jc = 1, phin = 1, sumn = 1, ans, s[M], n, prime[M], pn;
bool vis[M];
void seive (LL x){
for (LL i = 2; i <= x; i ++){
if (! vis[i])
prime[++ pn] = i;
for (LL j = 1; j <= pn && i * prime[j] <= x; j ++){
vis[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
LL qkpow (LL x, LL y){
LL sum = 1;
while (y > 0){
if (y % 2 == 1)
sum = sum * x % mod;
x = x * x % mod;
y /= 2;
}
return sum;
}
int main (){
scanf ("%lld", &n);
seive (n);
for (LL i = 1; i <= n; i ++)
n_jc = n_jc * i % mod;
for (LL j = 1; j <= pn; j ++)
for (LL k = prime[j]; k <= n; k *= prime[j])
s[j] += n / k;
for (LL i = 1; i <= pn; i ++){
phin = phin * qkpow (prime[i], s[i] - 1) % mod * (prime[i] - 1) % mod;
sumn = sumn * (s[i] + 1) % mod;
}
ans = (((((n_jc - phin) % mod + mod) % mod - sumn) % mod + mod) % mod + 1) % mod;
printf ("%lld\n", ans);
return 0;
}