牛牛的“质因数”(筛法,dfs)

题目描述

算数基本定理,又称唯一分解定理,算术基本定理可表述为:任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积。即N=p1e1⋅p2e2…pmem(p1<p2<…pm)N=p_1^{e_1}\cdot p_2{e_2}…p_m{e_m}(p_1 < p_2< … p_m)N=p1e1​​⋅p2e2​​…pmem​​(p1​<p2​<…pm​)
朴素的质因子分解算法就是利用了算数基本定理,依次枚举p判断N是否包含素因子p。

牛牛最近对于质因数分解产生了浓厚的兴趣。

牛牛定义了一个函数F(x),它表示将x做质因数分解后得到的数字从小到大升序排列,然后将其“拼接”成一个大整数。
例如1500=22355*5,F(1500)=223555。
牛牛现在想要知道∑i=2nF(i)\sum_{i=2}^{n}F(i)∑i=2n​F(i)的值。

由于这个结果非常大,所以你只用告诉牛牛最终答案对109+710^9+7109+7取余数的结果即可。
输入描述:

仅一行一个正整数n(2≤n≤4×106)n(2 \leq n \leq 4 \times 10^6)n(2≤n≤4×106)

输出描述:

仅一行,表示答案对109+710^9+7109+7取余数的结果。

示例1
输入

3

输出

5

说明

示例2
输入

10

输出

342

说明

F(2)=2
F(3)=3
F(4)=22
F(5)=5
F(6)=23
F(7)=7
F(8)=222
F(9)=33
F(10)=25
2+3+22+5+23+7+222+33+25=342

思路

很容易发现,因为遵循唯一分解性定理, 所以每个数都只有唯一的分解方式, 但看数据范围如果暴力肯定会超时, 我们发现对于一个分解方式 2 x 2 x 3 x3 , 只需在这个分解方式后面新增一个比3大的素数就可以拼成另外一个数了, 我们倘如能利用前面的分解来合成新的的数就可以了, 有两种思路 一种dp, 在筛的时候就记录答案, 另外一种是dfs来合成数。 但两种都有一个注意点, n <= 4e6 显然 2^22小于 4e6 所以long long 都不能存下,
知识点 如果 x % mod = y;
设 z * 10 + w = x, 则 (z * 10 % mod + w % mod) % mod = y;
详细见于代码
1.筛法————线性筛

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
#define ull  long long

using namespace std;
const int N=4e6+20,MOD=1e9+7;
int prime[N],lowprime[N];
string f[N];
bool vis[N];
int n;
ull ans=0;
ull to_ll(string ans1){
	ull tmp=0;
	for(int i = 0; i < ans1.size(); i++)
			tmp = tmp * 10 + ans1[i] - '0';
	return tmp;
}
void init(){
    vis[1]=true;
    for(int i=2;i<= n;i++){
        if(!vis[i]){
            prime[++prime[0]]=i;
			f[i] = to_string(i);
        }
		else{
			int mmod=lowprime[i];
			f[i] = to_string(mmod) + f[i/mmod];
		}
        for(int j=1;i*prime[j]< N;j++){
            vis[i*prime[j]]=true;
			lowprime[i*prime[j]]=prime[j];
            if(i%prime[j]==0)	break;
        }
		if(i==n)
			return ;
    }
}
int ans2[30];
int main(){
	cin>>n;
	init();
	for(int i = 2; i <= n; i++)
	{
		reverse(f[i].begin(), f[i].end());
		for(int j = 0; j < f[i].size(); j++)
		{
			ans2[j] += (f[i][j] - '0');
		}
	}
	//for(int i = 0; i < 29; i ++) cout << ans2[i] << " ";
	long long ans1 = 0;
	int maxx = 0;
	for(int i = 0; i < 29; i++)
	{
		ans2[i + 1] = ans2[i + 1] + ans2[i] / 10;
		ans2[i] %= 10;
		if(ans2[i] != 0) maxx = i;
	}
	for(int i = maxx; i >= 0; i--)
	{
		ans1 = (ans1 * 10 + ans2[i]) % MOD;
	}
	cout << ans1 << endl;

    return 0;
}
  1. dfs
#include <bits/stdc++.h>

using namespace std;
#define ll long long
const int N = 4e6 + 10;
ll mod = 1e9 + 7;
ll sum;
ll n;
int p[N], cnt, vis[N];
void get_p()
{
	vis[1] = 1;
	for(int i = 2; i <= N; i++)
	{
		if(vis[i] == 0)
		{
			p[++cnt] = i;
		}
		for(int j = 1; p[j] * i < N; j ++)
		{
			vis[p[j] * i] = 1;
			if(i % p[j] == 0) break;
		}
	}
}
ll cal(string s)
{
	ll t = 0;
	for(int i = 0; i < s.size(); i++)
	{
		t = (t * 10 + s[i] - '0') % mod;
	}
	return t;
}
void dfs(int x, string s, ll tmp)
{
	if(s != "")
		sum = (sum + cal(s)) % mod;
	for(int i = x; i <= cnt && p[i] * tmp <= n; i ++)
	{
		dfs(i, s + to_string(p[i]), tmp * p[i]);
	}
}
int main()
{
	get_p();
	cin >> n;
	dfs(1, "", 1);
	cout << sum << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值