题目描述
话说正在 $jmy$ 愁苦如何筹钱给大家买汽水的时候,他遇上了一位魔法师。魔法师希望 $jmy$ 能帮他破解魔法书的咒语。如果 $jmy$ 做到了,就帮他付所有买汽水的钱。
魔法书上画了一个完全图(每对不同的顶点之间有且只有一条边),每个点都有一个独一无二的 $[1,n]$ 内的编号,$jmy$ 的任务是要找到最小生成树,以此作为魔法树,从而破解咒语。
对于完全图的边 $(i,j)$ $(i≠j)$ 的边权恰好就等于 $i,j$ 两个数字的最大公约数。特别地,要作为魔法树,必须满足树指定某个点为根后,所有除根以外的节点的父亲的标号必须小于自身标号。
$jmy$ 一眼就看出了最小生成树的边权和。然而咒语却是最小生成树的个数。为了保证大家都有汽水喝,你能帮帮 $jmy$ 吗?
输入格式
一行一个整数 $N$,表示完全图的大小
输出格式
一行一个整数,表示对 $100000007$ 取模的结果
样例输入
3
样例输出
2
数据范围
对于 $10 \%$ 的数据,$N \leq 5$
对于 $30 \%$ 的数据,$N \leq 8$
对于 $40 \%$ 的数据,$N \leq 10$
对于 $70 \%$ 的数据,$N \leq 5000$
对于 $100 \%$ 的数据,$N \leq 20000$
解题思路
首先我们考虑最小生成树的权值和为多少。
$1$ 与其他任何数的最大公约数都为 $1$
不难发现最小生成树的权值和为 $n-1$,每条边边权必须为 $1$
对于每一个节点,我们考虑哪些节点 $a$ 可以是这个节点 $b$ 的父亲
也就是求解 $a < b$ 且 $gcd(a, b) = 1$ 的个数,相乘即可
就是线性筛欧拉函数QAQ
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 100000 + 10;
const int MOD = 1e8 + 7;
int n;
int prime[MAXN], cnt;
long long phi[MAXN];
bool isPrime[MAXN];
int main(){
scanf("%d", &n);
phi[1] = 1;
for(register int i=2; i<=n; i++){
if(!isPrime[i]){
prime[++cnt] = i;
phi[i] = i - 1;
}
for(register int j=1; j<=cnt; j++){
if(i * prime[j] > n)
break;
isPrime[i * prime[j]] = true;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
phi[i * prime[j]] %= MOD;
break;
}else
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
phi[i * prime[j]] %= MOD;
}
}
long long ans = 1;
for(register int i=1; i<=n; i++){
ans *= phi[i];
ans %= MOD;
}
printf("%lld", ans);
return 0;
}