狄利克雷卷积
文中
表示卷积
表示乘积
就是有两个积性函数
和
这个就叫做狄利克雷卷积
杜教筛
假如要求积性函数
的前
项和
就要找一个积性函数
使得
是个很好求得的
也就是说假如
,那么
这个是非常好求的,并且假设求出来就等于
然后就是杜教筛的正文了:
就是对等式的右边进行变换:
感觉这一步就是最关键的了,我看了好久都不是很懂,然后打了个表来验证
左边的公式感觉就是把每个数拆成他有的两个因子在那里乘,右边的公式感觉就是收集一下每个因子乘了哪些数
哇我真的是反应不过来这个啊T_T
然后后面的还好看懂,用
表示
的前
项和:
然后再把 这一项单独拿出来:
这样 的关系式就有了,只不过前面有个系数
再把左边那好算的一坨
写回来,假设他算出来等于
因此就是:
所以最终 就是:
然后如果 是个常数,那么计算 的时候要 数论分块 加速一哈
就是说 这个值在 这段区间内都是一样的
51nod 1239 欧拉函数前缀和
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1239
好现在来套公式做题练练手:
首先要找一个合适的
函数,来和轻易地求出
对于欧拉函数,找的是
函数
不懂怎么找到的,难道是凑的???
因为
也就是说
也就是那个结论:
证明阔以看 这位童鞋滴,他的第二种比较好理解
那我们的
所以我们这里的 就是
再来化简右边,把
函数换成
函数:
再把 这一项单独弄出来
而 函数是常函数一直等于1 ,就简化一下,最终就推出这个公式,左边=右边:
所以最终就是:
而这种就是属于阔以分块加速的~
#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
const int MOD=1e9+7;
const int inv2=5e8+4;
typedef long long LL;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//Å·Àº¯Êý£¬Å·Àº¯Êýǰ׺ºÍ
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
memset(vis,1,sizeof(vis));
phi[1]=1;
Sphi[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[i])
{
prime.push_back(i);
phi[i]=i-1;
}
for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
}
}
LL S(LL n)
{
if(n<=maxn-5)return Sphi[n];
if(Mp[n])return Mp[n];
LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
for(LL i=2,j;i<=n;i=j+1)
{
j=n/(n/i);
res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
res%=MOD;
}
res=(res+MOD)%MOD;
return Mp[n]=res;
}
int main()
{
PHI(maxn-5);
LL N;
while(cin>>N)
{
cout<<S(N)<<endl;
}
}
51nod 1244 莫比乌斯函数前缀和
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1244
这次选的
函数也是
函数
使用的性质是:
证明阔以看这里
也就是
这样子计算出的
那直接套公式阔以得到:
51nod 1237 最大公约数之和 V3
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1237
用
表示
的个数
那么其实就是求:
而怎么求 喃?很经典的一个就是用莫比乌斯反演,但是这样是 的
还有一个公式就是
为什么喃?
相当于枚举 然后 是小于 中与 互质的,那么这不就是 的个数么?
然后就是 的时候,相当于枚举 ,因此是两倍,最后减个 是因为 这个是计算进了欧拉函数中的,算了两次,所以要减去1
而我们要的是
的,这样处理一哈就行了:
于是公式就出来了:
这样看起来好像也是 的, 要遍历 到 ,但是我们阔以数论分块~~~
在 这一段内的 是相同的,而这一段的 也只是个等差数列非常好求,因此,就阔以写了~
#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
const int MOD=1e9+7;
const int inv2=5e8+4;
typedef long long LL;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//Å·Àº¯Êý£¬Å·Àº¯Êýǰ׺ºÍ
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
memset(vis,1,sizeof(vis));
phi[1]=1;
Sphi[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[i])
{
prime.push_back(i);
phi[i]=i-1;
}
for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
}
}
LL S(LL n)
{
if(n<=maxn-5)return Sphi[n];
if(Mp[n])return Mp[n];
LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
for(LL i=2,j;i<=n;i=j+1)
{
j=n/(n/i);
res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
res%=MOD;
}
res=(res+MOD)%MOD;
return Mp[n]=res;
}
int main()
{
PHI(maxn-5);
LL N;
while(cin>>N)
{
LL res=0;
for(LL d=1,j;d<=N;d=j+1)
{
j=N/(N/d);
LL t1=(d+j)%MOD;
LL t2=(j-d+1)%MOD;
LL t=t1*t2%MOD*inv2%MOD;
res+=t%MOD*(2LL*S(N/d)-1)%MOD;
res%=MOD;
}
res=(res+MOD)%MOD;
cout<<res<<endl;
}
}
51nod 1188 最大公约数之和 V2
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1188
还是从最古老的公式入手,就是想求一个数m与n以内的数互质的个数:
当 时,很明显就是欧拉函数的定义,而且也退化成了一个常见的狄利克雷卷积的结论:
这是求的 的方法,要是求的 怎么办喃,就是同时除以 就转换成了 的了,那么 内互质的个数就是
然后就是枚举因子 的时候只用枚举到 ,,因为如果 是n的因子,那么 肯定也是 n的因子,这样就不会超时了,这个很关键
因为做V2的时候脑子有点反应不过来,因此在V1这里先做个铺垫,就是求V1,如果数据范围改成1e6的话,就这个阔以改成筛法,感觉线性筛就有点考脑子了。
筛法只能过1e6的数据
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int MOD=1e9+7;
LL N,M;
vector<int>prime;
int phi[maxn];
bool vis[maxn];
map<LL,LL>Mp;
LL ans[maxn];
void PHI(int n)
{
memset(vis,1,sizeof(vis));
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[i])
{
prime.push_back(i);
phi[i]=i-1;
}
for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int main()
{
PHI(maxn-5);
for(LL d=1;d<=maxn-5;d++)//ö¾ÙÒò×Ó
{
for(LL i=1;d*i<=maxn-5;i++)
{
LL n=d*i;
ans[n]+=d*phi[i];//Ï൱ÓÚÊǶÔn=d*iÕâ¸öÊýµÄ¹±Ï×
}
}
int N;
while(cin>>N)
{
cout<<ans[N]<<endl;
}
}
理解了这个的话V2就好做了