题目描述
经过精灵族全力抵挡,精灵终于坚持到了联络系统的重建,于是精灵向人类求助,
大魔法师伊扎洛决定弓}用博士的最新科技来抗敌。
伊扎洛:“博士,还没好吗?”
博士:“只差一步了!只需要在正确的位置装上弹药就可以了!”
博士的最新科技是全新的炸弹,但是现在还需要一步装弹药的操作。博士的炸弹有
N!个位置可以装弹药(>.<),但是只有在正确的位置装上弹药才能启动,博士将
装弹药的位置编号为1到N!,一个位置i需要装弹药,当且仅当gcd(i, N!) ≠ 1且
N! mod i ≠ 0,现在博士不要求你求出所有装弹药位置,只需要你求出满足要求
的位置的数量就可以了。
数据范围
N <= 1000000
————————————分界线——————————————
题解
首先当我们看到这个数据范围的时候
N <= 1000000
再看到要求算n!
心里肯定是无数只草泥马跑过了吧
—————————————进入正题——————————————
放导弹的位置满足
gcd(i, N!) ≠ 1且 N! mod i ≠ 0
从这里面我们便可以推出
=>i 为 与 n! 不互质的数 and i不是n! 的因子
而我们要求的这个答案即n! - ans1 - ans2 + 1
(ans1 代表与n! 互质的数的个数,ans2为因子数,同时1被重复计算了,所以需要-1)
我们进行分部解决,将ans1与ans2分步解决
因子数
根据唯一分解定律, 任意数t = p1 ^ a1 + p2 ^ a2 + … + pn ^ an
t的因数个数为ans = Π (ai + 1)
根据Legendre定理
证明
使用这个定理,我们就可以找出a[i]了,当然,应该先Euler筛法走一波
for (int i = 1;i <= cnt; i++)
{
k = 1;
///一定记得将k清1啊
///在正数n!的素因子标准分解式中, 素数p的最高指数记作pn[n!]
///则pn[n!] = Σ(k >= 1) ceil(n / p ^ k)
for (int j = 1;k * prime[i] <= n; j++)
k *= prime[i] ,pn[i] += n / k;
///第j时即为prime[i] ^ j
}
然后我们就可以使用我们的唯一分解定理将n!的因子数筛出来
只需要枚举每一个质数即可
for (int i = 1;i <= cnt; i++)
ans2 = ans2 * (pn[i] + 1) % mod;
///性质
///根据唯一分解定律, 任意数t = p1^a1 + p2^a2 + ... + pi^ai
///t的因数个数为ans = Π (ai + 1)
与 n! 互质的数
回想一下,与n互质的数,不就是Euler函数φ(n)吗
那么n!的φ应该如何求呢
这个时候
我们需要调用Euler函数的另一种求法(单纯求一个)
int phi (int n)
{
int res = n;
for (int i = 2;i * i <= n; i++)
if (n % i == 0)
{
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1)
res = res / n * (n - 1);
return res;
}
在本题中我们需要求的是n!的phi
那我们就可以在每一次/i的时候,不进行除法操作(* i / i = 1)
所以便有了
for (int i = 2;i <= n; i++)
if (!vis[i])
ans1 = ans1 * (i - 1) % mod;
else
ans1 = ans1 * i % mod;
///原本应是 if (!vis[i])
/// ans1 = ans1 * (i - 1) / i % mod;
/// else
/// ans1 = ans1 * i / i = ans1;
///而此题中, 应为求的是阶乘, 故自带一个 * i
///套取公式
///我们便可以把它消掉
///然后就ok了
如此,我们就求出了ans1和ans2的值
最后
用n! - ans1 - ans2 + 1即可
注意事项
1.数据很大,注意取模
2.ans应该为正数,可以使用
ans = (ans % mod + mod) % mod;
附上全部代码:
#include <cstdio>
#include <cctype>
typedef long long ll;
template <class T>
void r(T &x)
///Only for positive number
{
x = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48) ,c = getchar();
}
const int mod = 1e9 + 7;
const int N = 1e6;
const int Pri = 78500;
ll cnt ,prime[Pri];
ll pn[Pri];
int n;
bool vis[N];
void sieve(int n)
{
for (int i = 2;i <= n; i++)
{
if (!vis[i])
prime[++cnt] = i;
for (int j = 1;i * prime[j] <= n; j++)
{
vis[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
ll hyz(ll x ,ll y)
{
ll ans = 1;
do
{
if (y & 1)
ans = ans * x % mod;
x = x * x % mod;
}while (y >>= 1);
return ans;
}
int main()
{
r(n);
sieve(n);
ll k = 1;
for (int i = 1;i <= cnt; i++)
{
k = 1;
///在正数n!的素因子标准分解式中, 素数p的最高指数记作pn[n!]
///则pn[n!] = Σ(k >= 1) ceil(n / p ^ k)
for (int j = 1;k * prime[i] <= n; j++)
k *= prime[i] ,pn[i] += n / k;
///第j时即为prime[i] ^ j
}
k = 1;
for (int i = 2;i <= n; i++)
k *= i ,k %= mod;
ll ans1 = 1 ,ans2 = 1;
///ans1表示n!以内的与n互质的数
///ans2表示n!的约数个数
for (int i = 2;i <= n; i++)
if (!vis[i])
ans1 = ans1 * 1ll * (i - 1) % mod;
else
ans1 = ans1 * 1ll * i % mod;
///原本应是 if (!vis[i])
/// ans1 = ans1 * (i - 1) / i % mod;
/// else
/// ans1 = ans1 * i / i = ans1;
///而此题中, 应为求的是阶乘, 故自带一个 * i
///套取公式
///我们便可以把它消掉
///然后就ok了
for (int i = 1;i <= cnt; i++)
ans2 = ans2 * (pn[i] + 1) % mod;
///性质
///根据唯一分解定律, 任意数t = p1^a1 + p2^a2 + ... + pi^ai
///t的因数个数为ans = Π (ai + 1)
k = k - ans1 - ans2 + 1;
///其中ans1表示n!以内的与n互质的数
///ans2表示n!的约数个数, 由于1被减了两次, 要加上一次
///其实就是:既然求不满足, 就把总的减去满足的再加上重复的就是解
k = (k % mod + mod) % mod;
///个数为正数
printf ("%lld" ,k);
return 0;
}