现在杜教筛好像是个人都会了 orz。

所以我写篇博客复习一下,这里不讲原理只讲优化技巧的和如何使用

先贴两个学习原理的博客:

http://www.cnblogs.com/abclzr/p/6242020.html
http://www.cnblogs.com/candy99/p/dj_s.html

然后我们来讲一下,怎么使用,下面均以要 求和的n为109 为讨论前提

一般取用来卷积的辅助函数g为恒等函数:g(n)=1 ,n=1,2,3,…………(有时取g(n)=mu(n) 啥的也有奇效,反正是玄学)

这时杜教筛的公式就能化简为:

杜教筛使用心得

 

杜教筛使用心得

 我们一般只用红框里的两个公式。

杜教筛不要求函数一定要有积性,而是要求两点

  1. f(i)在106以内的所有前缀和能在接近O(n)复杂度内全部处理出来。
  2. 狄利克雷卷积的前缀和——h(n) ,在1e9 内的任意一项h(m),能在O(sqrt(m)) 以内的复杂度内求出来。

使用步骤一:预处理106 以内的s[i]

这一步如果f(i)是积性函数,可以直接用线性筛法直接搞定,如果不是的话,就要可能要注意分析性质瞎搞了(比如搞一些预处理,使得f(i)可以O(1)求之类的骚操作)。

注意这里只是以106为例,因为后面的计算过程常数比较大,追求速度的话,最好要能处理到2*106

使用步骤二:写快速计算h函数第k项的函数 h(int k)

这里求h,有以下几种方法

  1. 用公式推出杜教筛使用心得的通项看是否有优良的性质。可以快速搞出前n项和

  2. 杜教筛使用心得打表找规律
  3. 杜教筛使用心得打表找规律

     

 使用步骤三:写个记忆化搜索计算s(n)

 设我们预处理出s[i]的范围是i∈[1,UB】。

通用的写法

若之前算过n 我们则返回我们记下的值,因为n比较大不能用数组直接存,最好用哈希表。

若n<=UB,我们返回预处理好的s[i]

否则使用公式杜教筛使用心得递归计算

要注意到(n/i) 只有2*sqrt(n) 不同的取值,直接用一下数论分块就行了(如果不知道啥是数论分块的话,你百度一下就行 了)

然后注意把值存到哈希表内。

代码如下

 1 long long S(long long n)
 2 {
 3     if(n<=UB)
 4     {
 5         return s[n];
 6     }
 7     long long ans=0;
 8     ans=table.find(n);///查询哈希表中是否有n
 9     if(ans==-1)
10     {
11         long long i,j;
12         ans=0;
13         for(i=2; i<=n; i++)
14         {
15             j=i;
16             i=n/(n/i);
17             ans+=(i-j+1)*S(n/j);
18         }
19         ans=(h(n)-ans);
20         table.insert(n,ans);/// 将n对应的值存入哈希表
21     }
22     return ans;
23 }
适合多组查询的哈希表写法

相关文章:

猜你喜欢
  • 2021-06-04
相关资源
相似解决方案