【总结】【筛法】Min_25筛

博客介绍了积性函数f(x)在i从1到n(n≤10^11左右)求和的算法。要求当p为质数时,F(P)能表示为多项式形式。算法需对每一项分别求和,通过手推公式和转移计算,还可利用性质预处理值以加快计算,同时给出解题套路和板子题。

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

简介:

对于积性函数f(x)f(x)f(x)

∑i=1i≤nf(i)(其中n≤1011左右)\sum_{i=1}^{i\leq n} f(i)(其中n\leq 10^{11}左右)i=1inf(i)n1011

必须满足的条件是:
当p为质数时,F(P)F(P)F(P)必须能表示为一个多项式的形式,即F(P)=A0+A1P+A2P2+……F(P)=A_0+A_1P+A_2P^2+……F(P)=A0+A1P+A2P2+

并且项数不能太多(因为每一项都要手写公式求和)


算法概述:

在简介中已经写到,对于每一项都要分别求和,因此假设我们现在求的是第k+1项,即f(P)=AkPkf(P)=A_kP^kf(P)=AkPk

g(i,j)=∑k为质数,或k的最小质因子>Pjf(k)g(i,j)=\sum_{k为质数,或k的最小质因子>P_j}f(k) g(i,j)=kk>Pjf(k)

那么,显然g(i,0)g(i,0)g(i,0)就是所有数的k次方和。
这玩意就得考手推公式了:比如0次方和=i,1次方和=i∗(i+1)2\frac {i*(i+1)} 22i(i+1)……

然后需要转移:
首先,要求i≥prj∗prji\geq pr_j*pr_jiprjprj
(否则g(i,j)=g(i,j−1)g(i,j)=g(i,j-1)g(i,j)=g(i,j1)
在这里插入图片描述
当然,暴力算还是会T的。

但是由于一个性质:(nab\frac{\frac {n} {a}} {b}ban的个数=nc\frac {n} {c}cn的个数,即不超过2n2\sqrt n2n种)
就可以预处理出可能的iPj\frac {i} {P_j}Pji的值。然后就能快速算了。

然后,设
在这里插入图片描述
(当i&lt;prji&lt; pr_ji<prj时,S(i,j)=0S(i,j)=0S(i,j)=0,因此,下面默认i≥prji\geq pr_jiprj
显然,其中的质数部分为:g(i,∣P∣)−∑k=1j−1f(Pk)g(i,|P|)-\sum_{k=1}^{j-1}f(P_k)g(i,P)k=1j1f(Pk)

然后也可以转移:
在这里插入图片描述

套路

1、对于一些题目,可能在某些特定位置不满足条件,此时可以先忽略,最后再特判这些位置。
2、有些函数在多项式中系数不为1,这时最好先忽略系数,最后来乘。

板子题

LOJ6053简单的函数

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 200010
#define MOD 1000000007
using namespace std;
typedef long long ll;
ll sqr,n;
bool isprime[MAXN];
int primes[MAXN],tot;
ll sum[MAXN];
void prepare(){
	for(int i=2;i<=sqr;i++){
		if(isprime[i]==0){
			primes[++tot]=i;
			sum[tot]=(sum[tot-1]+i)%MOD;
		}
		for(int j=1;1ll*i*primes[j]<=sqr;j++){
			isprime[i*primes[j]]=1;
			if(i%primes[j]==0)
				break;
		}
	}
}
ll w[MAXN],id1[MAXN],id2[MAXN],g[MAXN],h[MAXN];
int cnt;
void get_g(){
	ll las;
	for(ll i=1;i<=n;i=las+1){
		las=n/(n/i);
		w[++cnt]=n/i;
		if(w[cnt]<=sqr)
			id1[w[cnt]]=cnt;
		else
			id2[n/w[cnt]]=cnt;
		g[cnt]=(w[cnt]%MOD+2)*(w[cnt]%MOD-1)%MOD*((MOD+1ll)/2ll)%MOD;
		h[cnt]=w[cnt]%MOD-1;
	}
	for(int j=1;j<=tot;j++)
		for(int i=1;i<=cnt&&1ll*primes[j]*primes[j]<=w[i];i++){
			ll k=w[i]/primes[j];
			if(k<=sqr)
				k=id1[k];
			else
				k=id2[n/k];
			g[i]=(g[i]-1ll*primes[j]*(g[k]-sum[j-1])%MOD+MOD)%MOD;
			h[i]=(h[i]-1ll*(h[k]-(j-1))%MOD+MOD)%MOD;
		}
}
int S(ll x,int j){
	if(x<=1||primes[j]>x)
		return 0;
	int k1;
	if(x<=sqr)
		k1=id1[x];
	else
		k1=id2[n/x];
	ll res=((g[k1]-h[k1]-sum[j-1]+j-1)%MOD+MOD)%MOD;
	if(j==1)
		res=(res+2)%MOD;
	for(int k=j;k<=tot&&1ll*primes[k]*primes[k]<=x;k++){
		ll t=primes[k];
		for(int e=1;1ll*primes[k]*t<=x;e++,t*=primes[k]){
			res=((res+1ll*(primes[k]^e)*S(x/t,k+1)%MOD)%MOD+(primes[k]^(e+1)))%MOD;
		}
	}
	return res;
}
int main(){
	SF("%lld",&n);
	sqr=sqrt(n);
	prepare();
//	PF("[%lld %d]",sqr,tot);
	get_g();
	PF("%d",(S(n,1)+1)%MOD);
}
#include <iostream> #include <vector> #include <algorithm> // 添加 max_element 和 min_element 依赖 #include <cmath> using namespace std; // 1. 鸡兔同笼 bool chicken_rabbit(int heads, int legs, int &chickens, int &rabbits) { if (legs % 2 != 0 || legs < 2 * heads || legs > 4 * heads) return false; rabbits = (legs - 2 * heads) / 2; chickens = heads - rabbits; return (rabbits >= 0 && chickens >= 0); } // 2. 约瑟夫环 int josephus(int n, int m) { if (n == 1) return 0; return (josephus(n - 1, m) + m) % n; } // 3. 埃氏筛法 vector<int> sieve_of_eratosthenes(int n) { vector<bool> is_prime(n + 1, true); is_prime[0] = is_prime[1] = false; for (int i = 2; i <= sqrt(n); ++i) { if (is_prime[i]) { for (int j = i * i; j <= n; j += i) is_prime[j] = false; } } vector<int> primes; for (int i = 2; i <= n; ++i) if (is_prime[i]) primes.push_back(i); return primes; } // 4. 计数排序 vector<int> counting_sort(const vector<int>& arr) { if (arr.empty()) return {}; int max_val = *max_element(arr.begin(), arr.end()); int min_val = *min_element(arr.begin(), arr.end()); int range = max_val - min_val + 1; vector<int> count(range), output(arr.size()); for (int num : arr) count[num - min_val]++; for (int i = 1; i < range; ++i) count[i] += count[i - 1]; for (int i = arr.size() - 1; i >= 0; --i) { output[count[arr[i] - min_val] - 1] = arr[i]; count[arr[i] - min_val]--; } return output; } int main() { // 测试鸡兔同笼 int heads = 35, legs = 94; int chickens, rabbits; if (chicken_rabbit(heads, legs, chickens, rabbits)) { cout << "鸡:" << chickens << " 只,兔:" << rabbits << " 只" << endl; } else { cout << "无解" << endl; } // 测试约瑟夫环 int n = 7, m = 3; cout << "幸存者编号:" << josephus(n, m) + 1 << endl; // 测试埃氏筛法 int max_num = 30; vector<int> primes = sieve_of_eratosthenes(max_num); cout << "素数:"; for (int p : primes) cout << p << " "; cout << endl; // 测试计数排序 vector<int> arr = {4, 2, 2, 8, 3, 3, 1}; vector<int> sorted = counting_sort(arr); cout << "排序结果:"; for (int num : sorted) cout << num << " "; return 0; }这段代码的结果图。
最新发布
03-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值