• 「专题总结」莫比乌斯反演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

    一个人的数论:

    直接附原博客链接

  • 相关阅读:
    sdnu 1513 字符串翻转
    hdu-1559 最大子矩阵(二维树状数组模板题)
    hdu-1556 树状数组
    1049.饭盒
    1092.校门外的树
    1012.区间合并
    1054.数独
    1175.开心的金明 01背包
    空格(Space)的ASCII码值是:32
    js为lable和div赋值
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12119104.html
Copyright © 2020-2023  润新知