2017.3.19 约数个数和 失败总结

来源:互联网 发布:网络研修的收获 编辑:程序博客网 时间:2024/06/04 23:11

        这个题一眼上去应该不是很难,但除了求约数个数有个分解质因数的技巧之外就不会什么了;;

        本来想着预处理50000以内的约数个数和之后求个前缀和直接乘、、、可见多么蠢、、搞了2h后弃疗了


        结果题解是 要算gcd    (。_。)

           还有莫比乌斯反演、、、 …(⊙_⊙;)…


     好吧,先借机学一下莫比乌斯反演 、、

         其实这道题几乎就是为莫比乌斯而生的(板子题

                


似乎是函数的反函数、、

所以叫反演、、 (*゜ー゜*)

而这个μ函数就是传说中的的莫比乌斯函数;

要注意是F(n/d)

那这个要怎么用?




那此题来说,首先要证明这个式子:


为什么会和gcd有关?

若i和j互质,那么i*j就是对于d(mn)唯一的约数

如果不互质,那么i和j还能再拆出几个数,作为约数

如  i=4 j=6,n=8,j=12;;

i=2*2   j=2*3

那么   i*j=2*2*2*3

可以写出互质的8 * 3

在循环到i=8,j=3时会被算进去、


而这些拆出的数一定可以对答案贡献1

所以用了bool;;

然后原式相当于求1~n、1~m的和:


根据 sigma交换律 和 可以处理处的某个积性函数μ可以得出莫比乌斯反演,:

就是套公式,  【gcd(a,b)==1】= Σ(d|i,d|j)μ(d)             )


注意μ是根据d能否整除i、d能否整除j   对d的一个函数,返回数值

有人可能要问gcd的判断和    i、j、d的整除关系得到的函数有什么关系?

注意多了一个Σ:也就是说它是先用前面四坨Σ枚举了1~n、 1~m所有可能的约数、、而这最后一个Σ则是枚举约数的约数、、(当然,默认N<M)

若d既整除了i,又整除了j,则i和j不互质,它有公因数d、、、

然后就不会了


我们注意到会有很多重复,重复的个数为  下取整(N/i)  所以可以用乘法优化加法,干掉两个Σ、、


这是反演的一步通用优化


再从d(约数)的角度交换一下位置:



对于每个d都要乘每种可能的i、j搭配,但对于n、m只会出现n/d、m/d次,每次搭配又会产生n/(i*d)、 m/(j*d)个重复

   所以都可以乘起来再加


把最右边两项的d写到分子:


我们可以发现有两个不相关的项,可以独立处理:




而且这两个函数计算方式一毛一样,只是定义域值域不一样所以:



就可以化为:


分别处理f函数,再用线筛求出μ函数,就可以做了;;;




码:

#include<iostream>#include<cstdio>using namespace std;#define ll long longint mu[50001];ll f[50001],ans; int i,j,n,tot,su[50001],k,t,m;bool vis[50001];void eular(){mu[1]=1;for(int i=2;i<=50000;i++){if(!vis[i]){su[++tot]=i;mu[i]=-1;//质数统一为 -1 }for(j=1;i*su[j]<50000&&j<=tot;j++){       vis[i*su[j]]=1;   if(i%su[j])    {   mu[i*su[j]]=-mu[i];//合数如果没有>2的次方的质数就看元素个数(取反,即为(-1)^n)    }else    {   mu[i*su[j]]=0;//合数如果是p^n形式,mu就是0    break;   }}} for (i=2; i<=50000; i++) mu[i]+=mu[i-1];  //处理前缀和(因为题目有Σ) for (i=1; i<=50000; i++)  //计算f         for (j=1; j<=i; j=k+1){              k=i/(i/j);               //可能有点mengbi,k存的是最大的i/j,同 余数之和sum一题 f[i]+=(ll)(k-j+1)*(i/j);   //用乘法加速连加        }  }int main(){     scanf("%d",&t);eular();while(t--){ scanf("%d%d",&n,&m);ans=0;     for (i=1; i<=m && i<=n; i=j+1){//枚举p,计算贡献              j=min(m/(m/i),n/(n/i));  //同余数之和sum的计算方式             ans+=f[m/i]*f[n/i]*(mu[j]-mu[i-1]);//用公式+前缀和统计答案          }          if(t)printf("%lld\n",ans);          else printf("%lld",ans);}}


莫比乌斯反演就是一个渠道,可以通过这个渠道对看似没有办法化简的式子转化为若干子函数,这样复杂度就会降低、

但式子的转化极其灵活,必须确保每一步正确性,

可以说,能用莫比乌斯反演的题都需要极强的数学敏感度和缜密的推理

当然,暴力性价比高、



附一张反演函数计算法则:





0 0
原创粉丝点击