Uoj 188.Sanrd(min_25筛次大质因数之和)

在这里插入图片描述
题目大意:让你求 ∑ i = 1 n f ( i ) \sum_{i = 1} ^ nf(i) i=1nf(i) f ( i ) f(i) f(i) i i i 的次大质因数。


这道题的解法基于min_25筛的算法过程:
min_25筛不仅可以筛一些积性函数前缀和,也能筛一些非积性函数前缀和,它的算法思想都是将贡献分成两部分:质数部分和合数部分。

S ( n , j ) S(n,j) S(n,j) 表示 最小质因子大于 p j p_j pj的函数前缀和
对于质数部分通常都会转化成积性函数,然后筛 g ( n , p ) g(n,p) g(n,p)
对于合数部分,算法流程是枚举最小质因子以及最小质因子的幂次 p k e p_k^e pke,然后用 S ( ⌊ n p k e ⌋ , k ) S(\lfloor\frac{n}{p_k^e}\rfloor,k) S(pken,k)转移,无论是筛 g ( n , j ) g(n,j) g(n,j) 还是筛 S ( n , j ) S(n,j) S(n,j) 都是基于 dp的思想,当筛的函数是积性函数时:合数部分可以提取 f ( p k e ) f(p_k^e) f(pke),变成: ∑ k = j + 1 , p k < n ∑ e = 1 , p k e ≤ x f ( p k e ) ∗ ( S ( ⌊ n p k e ⌋ , k ) + [ e > 1 ] ) \sum_{k =j + 1,p_k<\sqrt n}\sum_{e = 1,p_k^e\leq x}f(p_k^e)*(S(\lfloor\frac{n}{p_k^e}\rfloor,k) + [e >1]) k=j+1,pk<n e=1,pkexf(pke)(S(pken,k)+[e>1])
这题是非积性函数,仍然可以用min_25筛枚举最小质因子的思想。
首先根据题意,质数部分贡献为0。
合数部分:枚举 最小质因子 p k p_k pk时, p k p_k pk有贡献当且仅当除掉 p k e p_k^e pke后的数是质数 或为 1,若不是质数,那么可以对 ⌊ n p k e ⌋ \lfloor\frac{n}{p_k^e}\rfloor pken进行递归继续拆解,也就是 S ( ⌊ n p k e ⌋ , j + 1 ) S(\lfloor\frac{n}{p_k^e}\rfloor,j + 1) S(pken,j+1),而 p k p_k pk 的贡献 = p k ∗ ( ∑ i = 1 ⌊ n p k e ⌋ [ i ∈ p r i m e , i > = p k ] ) p_k * (\sum_{i = 1}^{\lfloor\frac{n}{p_k^e}\rfloor}[i \in prime,i >= p_k]) pk(i=1pken[iprime,i>=pk]),括号内的部分也就是 [ 1 , ⌊ n p k e ⌋ ] [1,\lfloor\frac{n}{p_k^e}\rfloor] [1,pken]内大于等于 p k p_k pk 的质数的个数,这个显然可以用min_25筛,筛出 [ 1 , n ] [1,n] [1,n]的质数个数。


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 10;
bool ispri[maxn];
int tot,num;
ll pri[maxn],w[maxn],g[maxn],sqr,n;
ll id1[maxn],id2[maxn];
void sieve(int n) {
	ispri[0] = ispri[1] = true;
	num = 0;
	for(int i = 2; i <= n; i++) {
		if(!ispri[i]) pri[++num] = i;
		for(int j = 1; j <= num && i * pri[j] <= n; j++) {
			ispri[i * pri[j]] = true;
			if(i % pri[j] == 0) break;
		}
	}
}
ll S(ll n,ll x,ll y) {
	if(pri[y] > x) return 0;
	ll res = 0;
	for(int i = y + 1; i <= num && 1ll * pri[i] * pri[i] <= x; i++) {
		for(ll e = 1,pe = pri[i]; pe * pri[i] <= x; pe = pe * pri[i],e++) {
			ll t = x / pe;
			ll k = t <= sqr ? id1[t] : id2[n / t];
			res += S(n,x / pe,i) + pri[i] * (g[k] - i + 1);
		}
	}
	return res;
}
ll solve(ll n) {
	tot = 0;
	sqr = sqrt(n);
	for(ll i = 1,j; i <= n; i = j + 1) {
		j = n / (n / i);
		w[++tot] = n / i;
		g[tot] = n / i - 1;
		if(n / i <= sqr) id1[n / i] = tot;
		else id2[j] = tot;
	}
	for(int i = 1; i <= num; i++) {
		for(int j = 1; j <= tot && pri[i] * pri[i] <= w[j]; j++) {
			ll t = w[j] / pri[i];
			ll k = t <= sqr ? id1[t] : id2[n / t];
			g[j] -= g[k] - (i - 1);
		}
	}
	return S(n,n,0);
}
ll l,r;
int main() {
	sieve(maxn - 10);
	scanf("%lld%lld",&l,&r);
	printf("%lld\n",solve(r) - solve(l - 1));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值