[总结] min-25筛

再不写总结我又会忘掉啊啊啊啊啊啊啊啊啊

这个\(min-25\)筛主要用来求一个积性函数的前缀和,就像这样\[\sum_{i=1}^n f(i)\]
不过这个积性函数要满足两个条件:质数\(p\)的函数值\(f(p)\)有个多项式表达,\(f(p^c)\)能够快速计算

\(min-25\)筛求前缀和的过程可以看成把所有数贡献分成质数贡献和合数贡献计算(可能有\(1\)的贡献).先考虑质数的贡献

...

好像除了暴力没有更好的办法啊

不过可以发现一个数\(>\sqrt{n}\)的质因子最多只有一个,也就是任意合数至少会有一个\(\le\sqrt{n}\)的质因子,所以可以枚举\(\le\sqrt{n}\)的质因数筛掉合数的贡献.

后面记\(P\)为质数集合,\(P_j\)为从小到大第\(j\)个质数.这里设\(g(n,j)\)表示\(\le n\)的所有质数以及最小质因数\(>P_j\)的数的贡献.考虑从\(g(n,j-1)\)转移到\(g(n,j)\),如果\(P_j>\sqrt{n}\),那么所有数都是质数了,所以可以直接赋值过来;否则还要减去最小质因子\(=P_j\)的数贡献.先把所有这样的数\(/ P_j\),除完后\(i\)对应到\(\frac{i}{P_j}\),同时最小质因子也要满足\(\ge P_j\),所以这一部分就是\(f(P_j)g(\lfloor\frac{n}{P_j}\rfloor,j-1)\).不过这样也减去了一些\(<P_j\)的质数的贡献,要加回来,所以综上所述,转移可以这样写\[g(n,j)=\begin{cases}g(n,j-1)&P_j\ge \sqrt{n}\\ g(n,j-1)-f(P_j)(g(\lfloor\frac{n}{P_j}\rfloor,j-1)-g(P_j-1,j-1))&P_j< \sqrt{n}\end{cases}\]

那个\(g(P_j-1,j-1)\)就是\(\sum_{k=1}^{j-1}f(P_k)\)

可以发现\(g(n,|P|)\)就是所有\(\le n\)质数的贡献(记最大的\(\le \sqrt{n}\)的质数为\(P_m\),其实只要做到\(g(n,m)\)就行了)

然后考虑要求的,类似于上面过程,记\(S(n,j)\)\(\le n\)所有最小质因子\(\ge P_j\)的数的贡献.首先考虑质数的贡献,也就是\[\sum_{k=j}^{|P|}f(P_k)=g(n,m)-g(P_j-1,j-1)\]

然后是合数贡献.先枚举那些合数的最小质因子\(P_k\),然后枚举它的出现次数\(c\),那么剩下数的贡献是\(S(\lfloor\frac{n}{{P_k}^c}\rfloor,k+1)\),因为是积性函数,所以这一部分的乘上\(f({P_k}^c)\)就是对应的数的贡献.但是这样子没有算到\({P_k}^c\)的贡献,所以额外加上,转移可以这样写\[S(n,j)=g(n,m)-g(P_j-1,j-1)+\sum_{k\ge j}\sum_{c}f({P_k}^c)(S(\lfloor\frac{n}{{P_k}^c}\rfloor,k+1)+[c>1])\]

然后递归处理即可

还有就是做的时候,只有形如\(\lfloor\frac{n}{i}\rfloor\)这样的数才是有用的,所以可以给每个这样的数编号,就能用数组代替map

例题先咕了

转载于:https://www.cnblogs.com/smyjr/p/11083723.html

#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、付费专栏及课程。

余额充值