「专题总结」莫比乌斯反演1

基本都是半年前做的题。。。印象不深,所以从头开始推式子写代码。

除了《一个人的数论》是3周前刚做的整个思路还在于是就粘了之前的代码。

都是套路,就那么几样:初等和式变换(所谓改变枚举顺序),数论分块,前缀和优化,设置新变量改变表达方式,以及及时预处理。

莫比乌斯反演的基本形式是$f(n)=sumlimits_{i|n} g(i)$等价于$g(n)=sumlimits_{i|n} mu(i) f(frac{n}{i})$

然而其实最常用的就是$[n]=sumlimits_{i|n} mu(i)$

然而隔了半年再做这些题,几乎都没有$1A$,我是不是要废了。。。

所有除法均向下取整。

YY的GCD:

$Description:$

神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种傻×必然不会了,于是向你来请教……

$T = 10000, N, M <= 10000000$

$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} [gcd(i,j)=p]$

$=sumlimits_{p} sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} [gcd(i,j)]$

$=sumlimits_{p} sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} sumlimits_{d|i and d|j} mu(d)$

$=sumlimits_{p} sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}$

设$x=pd$

$=sumlimits_{x} frac{n}{x} frac{m}{x} sumlimits_{d|x and  frac{x}{d} is prime} mu(d)$

最后一个和式可以调和级数预处理,其余数论分块。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define S 10000005
 5 int p[S],pc,mu[S],cal[S];;char np[S];
 6 signed main(){
 7     int n,m,t;cin>>t;mu[1]=1;
 8     for(int i=2;i<S;++i){
 9         if(!np[i])p[++pc]=i,mu[i]=-1;
10         for(int j=1,x;x=i*p[j],j<=pc&&x<S;++j)
11             if(i%p[j])np[x]=1,mu[x]=-mu[i];
12             else {np[x]=1;mu[x]=0;break;}
13     }
14     for(int i=1;i<=pc;++i)for(int j=1,x=p[i];x<S;++j,x+=p[i])cal[x]+=mu[j];
15     for(int i=1;i<S;++i)cal[i]+=cal[i-1];
16     while(t--){cin>>n>>m;ll ans=0;if(n>m)n^=m^=n^=m;
17         for(int i=1,l,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans+=(0ll+cal[l]-cal[i-1])*N*M;
18         cout<<ans<<endl;
19     }
20 }
View Code

数表:

$Description:$

有一张N×m的数表,其第i行第j列(1 < =i < =礼,1 < =j < =m)的数值为能同时整除i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。

$n,m le 10^5, Q le 2 imes 10^4$

$a$很烦人,考虑如何处理。

其实就是有些数有贡献,有些数暂时没有贡献。

离线下来按照$a$排序,让数依次产生贡献即可。

设$f(x)=sumlimits_{i|x} i$。如果小于$a$则为$0$。

题目要求:$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} f(gcd(i,j))$

$=sumlimits_{p} f(p) sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} [gcd(i,j)]$

$=sumlimits_{p} f(p) sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}$

$=sumlimits_{x} frac{n}{x} frac{m}{x} sumlimits_{d|x} mu(d) f(frac{x}{d})$

后面前缀和预处理(是一边离线一边预处理,用树状数组维护即可),前面继续数论分块。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 priority_queue<pair<int,int> >q;
 4 #define S 100005
 5 #define u unsigned
 6 int mu[S],pc,p[S],qn;u int f[S],ans[S],t[S];char np[S];
 7 struct QS{
 8     int n,m,o,a;
 9     friend bool operator<(QS a,QS b){return a.a<b.a;}
10 }Q[S];
11 void add(int p,int w){for(;p<S;p+=p&-p)t[p]+=w;}
12 u int ask(int p,u int a=0){for(;p;p^=p&-p)a+=t[p];return a;}
13 void Add(int p,int w){for(int i=1;i*p<S;++i)add(i*p,mu[i]*w);}
14 int main(){mu[1]=1;
15     for(int i=2;i<S;++i){
16         if(!np[i])mu[p[++pc]=i]=-1;
17         for(int j=1,x;j<=pc&&(x=i*p[j])<S;++j)
18             if(i%p[j])np[x]=1,mu[x]=-mu[i];
19             else{mu[x]=0;np[x]=1;break;}
20     }for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)f[j]+=i;
21     for(int i=1;i<S;++i)q.push(make_pair(-f[i],i));
22     q.push(make_pair(-1000000007,0));
23     cin>>qn;for(int i=1;i<=qn;++i)cin>>Q[i].n>>Q[i].m>>Q[i].a,Q[i].o=i;
24     sort(Q+1,Q+1+qn);
25     for(int i=1;i<=qn;++i){
26         int n=Q[i].n,m=Q[i].m,o=Q[i].o,N,M,l;if(n>m)n^=m^=n^=m;
27         while(q.top().first>=-Q[i].a)Add(q.top().second,-q.top().first),q.pop();
28         for(int i=1;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans[o]+=(ask(l)-ask(i-1))*N*M;
29     }for(int i=1;i<=qn;++i)cout<<(ans[i]&(1u<<31)-1)<<endl;
30 }
View Code

DZY Loves Math:

$Description:$

对于正整数n,定义f(n)为n所含质因子的最大幂指数。例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0。
给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b)。

$T le 10000, 1 le a,b le 10^7$

$f$可以线筛。只要记录每个数最后一次出现的因子出现次数,最多的出现次数就行。

$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} f(gcd(i,j))$

$=sumlimits_{p} f(p) sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}$

设$x=pd$

$=sumlimits_{x} frac{n}{x} frac{m}{x} frac{d|x} f(d) mu(frac{x}{d})$

问题在于最后一个和式。数据范围很大$O(n ln   n)$过不去,考虑优化。

存在$O(n ln ln n)$的做法,不难想(不断提出最后一种质因子),不再赘述。

从含义上考虑,$mu$在某一个因子次数大于$1$时值就会变成$0$,而最后一个和式相当于在把$x$的所有质因子分成两部分给$f$和$mu$。

而$mu$每种因子最多只能接受$1$个,其余都要给$f$。

如果一个数,它存在一种质因子出现次数与其它质因子出现次数不同,那么不管其它因子如何分配,这个因子一定会使$mu$正一次负一次,贡献抵消,总值为$0$

剩下的情况就是所有质因子出现次数相同。考虑和式的值。

我们钦定某一种质因子分配给$mu$了$0$个还是$1$个。可以发现如果$f$值相同的情况下正负依旧抵消。

唯一不同的就是如果所有质因子都给$mu$分配了$1$个,那么此时$f$的值减小了$1$。

再考虑此时$mu$的正负,可知如果因子种数有奇数个$f=1$,否则$f=-1$。

这样就解决了最后一个和式,前面还是数论分块。

说了这么多,不如打个表。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 10000007
 4 int p[S],lt[S],pc;char np[S];short mc[S],mt[S],tt[S],f[S];
 5 int main(){
 6     for(int i=2;i<S;++i){
 7         if(!np[i])p[++pc]=i,lt[i]=mt[i]=mc[i]=tt[i]=1;
 8         for(int j=1,x;j<=pc&&(x=p[j]*i)<S;++j)
 9             if(i%p[j]){
10                 np[x]=lt[x]=1,tt[x]=tt[i]+1,mt[x]=mt[i],mc[x]=mc[i];
11                 if(mt[x]==1)mc[x]++;
12             }
13             else{
14                 lt[x]=lt[i]+1;tt[x]=tt[i]+1;np[x]=1;
15                 if(lt[x]>mt[i])mt[x]=lt[x],mc[x]=1;
16                 else if(lt[x]==mt[i])mt[x]=lt[x],mc[x]=mc[i]+1;
17                 else mt[x]=mt[i],mc[x]=mc[i];
18                 break;
19             }
20     }for(int i=2;i<S;++i)f[i]=f[i-1]+(tt[i]==mt[i]*mc[i])*(mc[i]&1?1:-1);
21     int t,n,m,N,M;long long ans=0;cin>>t;while(t--){
22         cin>>n>>m;if(n>m)n^=m^=n^=m;
23         for(int i=1,l;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans+=1ll*N*M*(f[l]-f[i-1]);
24         cout<<ans<<endl;ans=0;
25     }
26 }
View Code

约数个数和:

$Description:$

设$d(x)$为$x$的约数个数,给定$N$、$M$,求$sumlimits_{i=1}^{N} sumlimits_{j=1}^{M} d(ij)$

$1<=N, M<=50000,1<=T<=50000$

结论题。这就很难受了。

$d(nm)=sumlimits_{x|n}sumlimits_{y|m} [gcd(x,y)]$

证明网上到处都是。记结论吧。

$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} d(ij)$

$=sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} sumlimits_{x=1}^{i} sumlimits_{y=1}^{j} [gcd(i,j)]$

$=sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} sumlimits_{x=1}^{i} sumlimits_{y=1}^{j} sumlimits_{d|x and d|y} mu(d)$

$=sumlimits_{d} mu(d) sumlimits_{x=1}^{frac{n}{d}} frac{n}{dx} sumlimits_{y=1}^{frac{m}{d}}frac{m}{dy}$

后面的两个和式分别只与$frac{n}{d}$和$frac{m}{d}$有关,设为函数$f(x)$

$=sumlimits_{d} mu(d) f(frac{n}{d}) f(frac{m}{d})$

预处理$f$,数论分块。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 50005
 4 int mu[S],p[S],pc,f[S];char np[S];
 5 int main(){mu[1]=1;
 6     for(int i=2;i<S;++i){
 7         if(!np[i])mu[i]=-1,p[++pc]=i;
 8         for(int j=1,x;x=i*p[j],j<=pc&&x<S;++j)
 9             if(i%p[j])mu[x]=-mu[i],np[x]=1;
10             else {np[x]=1;mu[x]=0;break;}
11     }
12     for(int i=1;i<S;++i)for(int j=1,l,x;x=i/j,j<=i;j=l+1)l=i/x,f[i]+=x*(l-j+1);
13     for(int i=1;i<S;++i)mu[i]+=mu[i-1];
14     int t,n,m;cin>>t;while(t--){
15         long long ans=0;cin>>n>>m;if(n>m)n^=m^=n^=m;
16         for(int i=1,l,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans+=(0ll+mu[l]-mu[i-1])*f[N]*f[M];
17         cout<<ans<<endl;
18     }
19 }
View Code

数字表格:

$Description:$

Doris刚刚学习了fibonacci数列。用f[i]表示数列的第i项,那么
f[0]=0
f[1]=1
f[n]=f[n-1]+f[n-2],n>=2
Doris用老师的超级计算机生成了一个n×m的表格,第i行第j列的格子中的数是f[gcd(i,j)],其中gcd(i,j)表示i,j的最大公约数。

Doris的表格中共有n×m个数,她想知道这些数的乘积是多少。答案对$10^9+7$取模。

奇怪的题目?为什么是累乘而不是求和啊?

先别着急上原根。。。好像指数就可以直接求和的样子。。。

$prodlimits_{i=1}^{n} prodlimits_{j=1}^{m} fib(gcd(i,j))$

$=prodlimits_{p} fib(p)^{ sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} [gcd(i,j)]  }$

$=prodlimits_{p} fib(p)^{ sumlimits_{d} mu(d) frac{n}{pd} frac{m}{pd}  }$

$=prodlimits_{x} (prodlimits_{d|x} fib(d)^{ mu(frac{x}{d} ) } )^{frac{n}{x} frac{m}{x}}$

还是数论分块,加和乘没有本质区别。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 1000005
 4 #define mod 1000000007
 5 int pw(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 6 int fib[S],f[S],mu[S],p[S],pc,iv[S];char np[S];
 7 int main(){mu[1]=f[1]=fib[1]=f[0]=1;
 8     for(int i=2;i<S;++i){
 9         if(!np[i])p[++pc]=i,mu[i]=-1;
10         for(int j=1,x;j<=pc&&(x=p[j]*i)<S;++j)
11             if(i%p[j])np[x]=1,mu[x]=-mu[i];
12             else {np[x]=1;mu[x]=0;break;}
13     }for(int i=2;i<S;++i)f[i]=1,fib[i]=(fib[i-1]+fib[i-2])%mod;
14     for(int i=1;i<S;++i)iv[i]=pw(fib[i],mod-2);
15     for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)if(mu[j/i])f[j]=1ll*f[j]*(mu[j/i]>0?fib[i]:iv[i])%mod;
16     for(int i=2;i<S;++i)f[i]=1ll*f[i-1]*f[i]%mod;
17     int t,n,m,ans=1,N,M,l;cin>>t;while(t--){
18         cin>>n>>m;if(n>m)n^=m^=n^=m;
19         for(int i=1;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans=1ll*ans*pw(1ll*f[l]*pw(f[i-1],mod-2)%mod,1ll*N*M%(mod-1))%mod;
20         cout<<ans<<endl;ans=1;
21     }
22 }
View Code

于神之怒加强版:

$Description:$

给下N,M,K.求$sumlimits_{i=1}^{N} sumlimits_{j=1}^{M} gcd(i,j)^K$

$1 le N,M,K le 5000000,1 le T le 2000$

$sumlimits_{i=1}^{N} sumlimits_{j=1}^{M} gcd(i,j)^K$

$=sumlimits_{p} p^K sumlimits_{i=1}^{frac{N}{x}} sumlimits_{j=1}^{frac{M}{x}} [gcd(i,j)]$

$=sumlimits_{p} p^K sumlimits_{d} mu(d) frac{N}{pd} frac{M}{pd}$

$=sumlimits_{x} frac{N}{x} frac{M}{x} sumlimits_{p|x} p^K  imes mu(frac{x}{p})$

后者是个积性函数,线筛。然后除法分块。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 1000000007
 4 #define S 5000005
 5 int pow(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 6 int f[S],p[S],pc,pw[S];char np[S];
 7 int main(){
 8     int t,k,n,m;cin>>t>>k;f[1]=1;
 9     for(int i=2;i<S;++i){
10         if(!np[i])p[++pc]=i,f[i]=(pw[i]=pow(i,k))-1;
11         for(int j=1,x;j<=pc&&(x=i*p[j])<S;++j)
12             if(i%p[j])f[x]=1ll*f[i]*f[p[j]]%mod,np[x]=1;
13             else {f[x]=1ll*f[i]*pw[p[j]]%mod;np[x]=1;break;}
14     }
15     for(int i=2;i<S;++i)f[i]=(f[i]+f[i-1])%mod;
16     while(t--){cin>>n>>m;if(n>m)n^=m^=n^=m;int ans=0;
17         for(int i=1,l,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans=(ans+1ll*N*M%mod*(f[l]-f[i-1]+mod))%mod;
18         cout<<ans<<endl;
19     }
20 }
View Code

jzptab:

$Description:$

求$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} lcm(i,j)$。答案模100000009输出多组询问

$T le 10^4,n,m le 10^7$

$sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} lcm(i,j)$

$=sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} frac{i imes j}{gcd(i,j)}$

$=sumlimits_{p} sumlimits_{i=1}^{frac{n}{p}} sumlimits_{j=1}^{frac{m}{p}} i imes j imes p sumlimits_{d|i and d|j} mu(d)$

$=sumlimits_{p} sumlimits_{d} mu(d) d^2 sumlimits_{i=1}^{frac{n}{pd}} i imes sumlimits_{j=1}^{frac{m}{pd}} j$

后面两个都是等差数列求和,设为$f(x)=sumlimits_{i=1}^{x}i $

$=sumlimits_{x} f(frac{n}{x}) imes f(frac{m}{x}) imes x sumlimits_{d|x} mu(d) imes d$

最后一个和式又可以线筛了。等差数列求和不用说,要把$x$算进后面的那个和式作为一个函数求前缀和比较方便。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 100000009
 4 #define S 10000005
 5 int p[S],pc,f[S],g[S];char np[S];
 6 int main(){g[1]=1;
 7     for(int i=2;i<S;++i){
 8         if(!np[i])p[++pc]=i,g[i]=mod-i+1;
 9         for(int j=1,x;j<=pc&&(x=p[j]*i)<S;++j)
10             if(i%p[j])g[x]=1ll*g[i]*g[p[j]]%mod,np[x]=1;
11             else {np[x]=1;g[x]=g[i];break;}
12     }for(int i=1;i<S;++i)g[i]=(1ll*g[i]*i+g[i-1])%mod,f[i]=(f[i-1]+i)%mod;
13     int t,n,m,N,M,l;long long ans=0;cin>>t;while(t--){
14         cin>>n>>m;if(n>m)n^=m^=n^=m;
15         for(int i=1;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans=(ans+1ll*f[N]*f[M]%mod*(g[l]-g[i-1]+mod))%mod;
16         cout<<ans<<endl;ans=0;
17     }
18 }
View Code

一个人的数论:

直接附原博客链接

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12119104.html