min_25 JZOJ5594 最大真因数

本文介绍了一种高效算法,用于计算指定区间内所有合数的最大真因数之和。采用埃氏筛法原理,通过优化实现,解决了大范围内的合数真因数求和问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Time Limits: 2000 ms Memory Limits: 524288 KB Detailed Limits Downloads

Description

一个合数的真因数是指这个数不包括其本身的所有因数,例如6 的正因数有
1; 2; 3; 6,其中真因数有1; 2; 3。一个合数的最大真因数则是这个数的所有真因数中最大
的一个,例如6 的最大真因数为3。
给定正整数l 和r,请你求出l 和r 之间(包括l 和r)所有合数的最大真因数之和。

Input

从文件factor.in 中读入数据。
输入共一行,包含两个正整数l 和r。保证l<=r。

Output

输出到文件factor.out 中。
输出共一行,包含一个整数,表示[l; r] 内所有合数的最大真因数之和。

Sample Input

1 10
101 1000
180208 975313
339762200 340762189
2500000000 5000000000

Sample Output

17
163446
151642139152
112318862921546
3094668961678105770

Data Constraint

l,r<=5*10^9

Hint

【样例1 解释】
在1 至10 之间的合数有4; 6; 8; 9; 10,它们的最大真因数分别为2; 3; 4; 3; 5,因此最
大真因数之和为2 + 3 + 4 + 3 + 5 = 17

题解

首先肯定先拆成1–l-1,1–r
考虑模拟埃氏筛法:f[i][j]表示当前要筛2–i,已经筛完了前j个质数剩下的数的和
设最后一个能筛带质数的数是ptpt
那么答案就为ti=1(f[n][i]f[n1][i])/pi∑i=1t(f[n][i]−f[n−1][i])/pi
考虑转移
首先p2j>ipj2>i时筛不掉任何数,所以此时f[i][j]=f[i][j-1]
否则我们考虑对其可以筛掉的数分解质因数,发现分解后每一个数对应的质因数组都是大于等于pj的,并且一定有pj,所以f[i][j]=f[i-1][j]-pj*(f[i/pj][j-1]-sum[j-1]),其中sum[x]为前x个质数的和
这样就相当于强行选了一个pj,然后减去那个前缀和的原因是不能有更小的质因数
发现第一维的范围是nn级别的,因为<nn的有nn个,对于i>=nn,n/i<=nn,也就是说最多只有nn个可能会用到的i,然后因为第二维是质数所以还可以除一个log,最后复杂度是n3/4(1/logn)n3/4∗(1/logn)
在实际的实现中,我们可以按照j分层做,随着j的增长,第一位是递减的,对于小于nn的我们开一个数组直接存,大于nn的i我们可以存储n/i,这样就可以直接转移了

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long

using namespace std;

const int maxn=4e5+5;

int f[2][maxn];
int st[maxn],tp;
ll go[maxn],s[maxn],sum[maxn];
bool bz[maxn];
int i,j,k,m;
ll l,r,x,y,t1,t2,now,ans;

ll solve(ll x){
    m=trunc(sqrt(x)); tp=0; ans=0;
    int i,j;
    memset(bz,false,sizeof(bz));
    fo(i,2,m){
        if (bz[i]==false){
            st[++tp]=i; sum[tp]=sum[tp-1]+i;
        }
        fo(j,1,tp){
            if (st[j]*i>m) break;
            bz[st[j]*i]=true;
            if (i%st[j]==0) break;
        }
    }
    ll l=1; now=0;
    while (l<=x){
        t1=x/l;
        s[++now]=(t1*(t1+1))/2-1;
        if (t1<=m) f[0][t1]=now; else f[1][l]=now;
        go[now]=t1;
        l=(x/t1)+1;
    }
    fo(j,1,tp){
        while (go[now]/(st[j]*st[j])==0) now--;
        fo(i,1,now){
            t1=go[i]/st[j];
            if (t1<=m) t2=f[0][t1]; else t2=f[1][x/t1];
            if (i==1) ans=ans+s[t2]-sum[j-1];
            s[i]=s[i]-(s[t2]-sum[j-1])*st[j];
        }
    }
    return ans;
}
int main(){
    freopen("factor.in","r",stdin);
    freopen("factor.out","w",stdout);
    scanf("%lld%lld",&l,&r);
    printf("%lld\n",solve(r)-solve(l-1));
    return 0;
}
给定区间内计算每个数的最大因数的个数,并优化时间复杂度,可以通过以下方法实现: ### 1. 预处理最小质因数 首先,可以利用线性筛法(埃拉托斯特尼筛法的优化版本)预处理出每个数的最小质因数。这种方法的时间复杂度为 $O(n)$,适用于较大范围的区间。在预处理完成后,可以快速分解每个数的质因数,并提取最大因数。 以下是一个实现最小质因数预处理的代码示例: ```cpp #include <iostream> #include <vector> using namespace std; const int N = 1000010; // 最大范围 int min_prime[N]; // 存储每个数的最小质因数 vector<int> primes; // 存储质数 void preprocess() { for (int i = 2; i < N; ++i) { if (min_prime[i] == 0) { // 如果i是质数 min_prime[i] = i; // 自己是最小质因数 primes.push_back(i); // 加入质数列表 } for (int j = 0; j < primes.size() && primes[j] <= min_prime[i] && i * primes[j] < N; ++j) { min_prime[i * primes[j]] = primes[j]; // 标记i * primes[j]的最小质因数 } } } ``` ### 2. 分解并提取最大因数 通过预处理得到的最小质因数数组,可以高效地分解每个数的质因数。例如,对于一个数 $x$,可以通过不断除以它的最小质因数,直到 $x = 1$ 为止,从而得到所有质因数。在这个过程中,记录最大的质因数即可。 以下是一个分解每个数并计算最大因数的代码示例: ```cpp int max_prime_factor(int x) { int max_factor = 0; while (x > 1) { int factor = min_prime[x]; // 获取最小质因数 max_factor = max(max_factor, factor); // 更新最大因数 while (x % factor == 0) { x /= factor; // 除以该质因数 } } return max_factor; } ``` ### 3. 统计区间内每个数的最大因数个数 在预处理分解的基础上,可以遍历给定区间内的每个数,计算其最大因数,并统计其出现次数。例如,对于区间 $[l, r]$,可以使用一个哈希表或数组来记录每个最大因数的出现次数。 以下是一个完整的统计代码示例: ```cpp #include <iostream> #include <vector> #include <unordered_map> using namespace std; const int N = 1000010; // 最大范围 int min_prime[N]; // 存储每个数的最小质因数 unordered_map<int, int> count_map; // 记录最大因数的出现次数 // 预处理最小质因数 void preprocess() { for (int i = 2; i < N; ++i) { if (min_prime[i] == 0) { // 如果i是质数 min_prime[i] = i; // 自己是最小质因数 for (long long j = (long long)i * i; j < N; j += i) { if (min_prime[j] == 0) { min_prime[j] = i; } } } } } // 计算x的最大因数 int max_prime_factor(int x) { int max_factor = 0; while (x > 1) { int factor = min_prime[x]; // 获取最小质因数 max_factor = max(max_factor, factor); // 更新最大因数 while (x % factor == 0) { x /= factor; // 除以该质因数 } } return max_factor; } // 统计区间[l, r]内每个数的最大因数的出现次数 void count_max_prime_factors(int l, int r) { for (int i = l; i <= r; ++i) { int max_factor = max_prime_factor(i); count_map[max_factor]++; } } int main() { preprocess(); // 预处理最小质因数 int l, r; cin >> l >> r; count_max_prime_factors(l, r); // 统计最大因数 for (auto &entry : count_map) { cout << "最大因数 " << entry.first << " 出现了 " << entry.second << " 次" << endl; } return 0; } ``` ### 4. 时间复杂度分析 - **预处理最小质因数**:时间复杂度为 $O(n)$,其中 $n$ 是最大范围。 - **分解每个数的最大因数**:对于每个数 $x$,分解的时间复杂度与质因数的数量相关,平均情况下为 $O(\log x)$。 - **统计区间内每个数的最大因数**:时间复杂度为 $O(r - l + 1) \cdot O(\log x)$,其中 $r - l + 1$ 是区间长度。 综上,整个算法的时间复杂度接近 $O(n + (r - l + 1) \cdot \log x)$,适用于较大范围的区间。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值