输入描述:
仅一行一个正整数 n(2 <= n <=4*10^6)
输出描述:
仅一行,表示答案对10^9+7取余的结果
输入:
样例1
3
样例2
10
输出:
样例1
5
样例2
342
思路: 核心:素数筛+dp
将一个数分解为最小质因数的乘积再拼成一个大数,如果暴力的话必超时。
不妨将拼凑的过程整体化,即1+1=2,两个部分拼成一个完整部分。
可以发现,对于数字n,去拼成它的大数f[n]时,末尾必是n的最大素数,易得f[n]=f[n/最大素数]+n的最大素数,用dp去想,豁然开朗。
这里dp可以加在素数筛里,很舒服(我用的埃氏筛,当然线性筛也可以,可能写的代码稍微长一点 =……=
// 素筛dp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p=1e9+7;
ll f[10000000];//每个数的处理后的大整数
ll cal(int x) //计算10的x次方
{
int ans=10;
while(x--)
{
ans*=10;
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n;
cin>>n;
//埃氏筛
for(int i=2; i<=n; i++)
{
if( !f[i] )//如果是素数
{
f[i]=i; //最大素数就是本身
//dp,用对数求要对f[j/i]乘多少个10,因为末尾素数可能很大,前面的数字要前移的位数等于末尾素数的位数
for(int j=i+i ;j<=n ;j+=i) f[j]=( f[j/i] * cal(log10(i))%p + i)%p;
}
}
ll ans=0;
for(int i=2 ; i<=n ;i++) //最后求和
{
ans= ( ans+f[i] ) %p;
}
cout<<ans<<endl;
}