[模板]杜教筛

用途

 比线性更快($O(n^{\frac{2}{3}})$)地求某些积性函数的前缀和

前置知识:狄利克雷卷积

形如$h(n)=\sum\limits_{d|n}f(d)g(\frac{n}{d})$,则称$h(n)=f(x)*g(x)$

如果f和g都是积性函数,则卷出的h也是积性函数

可以证明,狄利克雷卷积满足交换律、结合律、分配律

比较重要的卷积式子(抄的..):

$$\mu*1=\varepsilon , \varepsilon(n)=[n=1]$$

$$\varphi*1=id , id(n)=n $$

$$id*\mu=\varphi$$

$$id*1=\sigma , \sigma表示因数和$$

 

做法

设要求的是$\sum{f(i)}$

构造积性函数$h,g$,使得$h=g*f$,而且需要容易求出$h$和$g$的前缀和

推式子:设S(n)是f的前缀和(以下不是整除的都向下取整)

$$\sum\limits_{i=1}^{n}h(i)=\sum\limits_{i=1}^{n}\sum\limits_{d|i}g(d)f(\frac{n}{d})$$

$$\sum\limits_{i=1}^{n}h(i)=\sum\limits_{d=1}^{n}g(d)\sum\limits_{i=1}^{\frac{n}{d}}f(i)$$

$$\sum\limits_{i=1}^{n}h(i)=\sum\limits_{d=1}^{n}g(d)S(\frac{n}{d})$$

$$把后面的第一项提出来,\sum\limits_{i=1}^{n}h(i)=g(1)S(n)+\sum\limits_{d=2}^{n}g(d)S(\frac{n}{d})$$

$$g(1)S(n)=\sum\limits_{i=1}^{n}h(i)-\sum\limits_{d=2}^{n}g(d)S(\frac{n}{d})$$

于是就可以整除分块,然后递归地求S了(需要先手动求出比较小范围内的S)

需要记忆化,不想带log的话可以用unordered_map(c++11或者tr1/unordered_map)或者手写hash

而且一开始求的那些S不要放到map里,单开一个数组存,不然常数会很大

 

所以主要是构造比较难构造,看着上面的卷积式瞎怼吧...

例题

bzoj4916 神犇和蒟蒻

注意到$\varphi (i^2) = i\varphi(i)$

然后构造$g=id , h=id^2$即可

 1 #include<bits/stdc++.h>
 2 #include<tr1/unordered_map>
 3 #define CLR(a,x) memset(a,x,sizeof(a))
 4 #define MP make_pair
 5 using namespace std;
 6 typedef long long ll;
 7 typedef unsigned long long ull;
 8 typedef pair<int,int> pa;
 9 const int maxn=1e5+10,P=1e9+7,inv6=166666668;
10 
11 inline ll rd(){
12     ll x=0;char c=getchar();int neg=1;
13     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
14     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
15     return x*neg;
16 }
17 
18 int N,phi[maxn],pri[maxn],cnt;
19 bool np[maxn];
20 tr1::unordered_map<int,int> sum;
21 
22 inline int calc(int n){
23     if(sum.count(n)) return sum[n];
24     ll re=0;
25     re=1ll*n*(n+1)%P*(2*n+1)%P*inv6%P;
26     for(int i=2,j;i<=n;i=j+1){
27         j=n/(n/i);
28         re=(re-1ll*(j-i+1)*(j+i)/2%P*calc(n/i))%P;
29     }
30     sum[n]=re;
31     return re;
32 }
33 
34 int main(){
35     //freopen("","r",stdin);
36     int i,j,k;
37     phi[1]=1;
38     for(i=2;i<=1e5;i++){
39         if(!np[i]){
40             pri[++cnt]=i;
41             phi[i]=i-1;
42         }for(j=1;j<=cnt&&pri[j]*i<=1e5;j++){
43             np[i*pri[j]]=1;
44             if(i%pri[j]==0){
45                 phi[i*pri[j]]=phi[i]*pri[j];
46                 break;
47             }
48             phi[i*pri[j]]=phi[i]*phi[pri[j]];
49         }
50     }
51     sum[0]=0;
52     for(i=1;i<=1e5;i++){
53         sum[i]=(sum[i-1]+1ll*i*phi[i])%P;
54     }
55     printf("1\n%d\n",(calc(rd())+P)%P);
56     return 0;
57 }

 

posted @ 2019-01-06 12:53  Ressed  阅读(189)  评论(0编辑  收藏  举报