BZOJ3561 DZY Loves Math VI【莫比乌斯反演】

本文深入解析洛谷P1829[国家集训队]Crash的数字表格问题,通过数学推导和莫比乌斯反演技巧,详细解释了如何高效求解特定数学表达式的模运算,适用于竞赛编程中类似问题的解决。

Time Limit: 10 Sec
Memory Limit: 256 MB

Description

给定正整数n,m。求
在这里插入图片描述

Input

一行两个整数n,m

Output

一个整数,为答案模1000000007后的值

HINT

1<=n,m<=500000,共有3组数据


题目分析

和 洛谷P1829 [国家集训队]Crash的数字表格的推导过程很像

∑i=1n∑j=1mlcm(i,j)gcd(i,j)=∑i=1n∑j=1m(ijgcd(i,j))gcd(i,j)\sum_{i=1}^n\sum_{j=1}^mlcm(i,j)^{gcd(i,j)}=\sum_{i=1}^n\sum_{j=1}^m(\frac{ij}{gcd(i,j)})^{gcd(i,j)}i=1nj=1mlcm(i,j)gcd(i,j)=i=1nj=1m(gcd(i,j)ij)gcd(i,j)

把gcd提出来枚举

∑d=1min(n,m)∑i=1n∑j=1m(ijd)d[gcd(i,j)=d]\sum_{d=1}^{min(n,m)}\sum_{i=1}^n\sum_{j=1}^m(\frac{ij}{d})^{d}[gcd(i,j)=d]d=1min(n,m)i=1nj=1m(dij)d[gcd(i,j)=d]

gcd(i,j)==dgcd(i,j)==dgcd(i,j)==d,则有gcd(i/d,j/d)=1gcd(i/d,j/d)=1gcd(i/d,j/d)=1

∑d=1min(n,m)∑x=1⌊nd⌋∑y=1⌊md⌋(xd∗ydd)d[gcd(x,y)=1]=∑d=1min(n,m)dd∑x=1⌊nd⌋∑y=1⌊md⌋(xy)d[gcd(x,y)=1]\sum_{d=1}^{min(n,m)}\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}(\frac{xd*yd}{d})^{d}[gcd(x,y)=1]=\sum_{d=1}^{min(n,m)}d^d\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}(xy)^{d}[gcd(x,y)=1]d=1min(n,m)x=1dny=1dm(dxdyd)d[gcd(x,y)=1]=d=1min(n,m)ddx=1dny=1dm(xy)d[gcd(x,y)=1]

莫比乌斯反演一下

∑d=1min(n,m)dd∑x=1⌊nd⌋∑y=1⌊md⌋(xy)d∑z∣gcd(x,y)μ(z)=∑d=1min(n,m)dd∑x=1⌊nd⌋∑y=1⌊md⌋(xy)d∑z=1gcd(x,y)μ(z)[z∣gcd(x,y)]\sum_{d=1}^{min(n,m)}d^d\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}(xy)^{d}\sum_{z|gcd(x,y)}\mu(z)=\sum_{d=1}^{min(n,m)}d^d\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}(xy)^{d}\sum_{z=1}^{gcd(x,y)}\mu(z)[z|gcd(x,y)]d=1min(n,m)ddx=1dny=1dm(xy)dzgcd(x,y)μ(z)=d=1min(n,m)ddx=1dny=1dm(xy)dz=1gcd(x,y)μ(z)[zgcd(x,y)]

变换一下枚举顺序
∑d=1min(n,m)dd∑z=1min(⌊nd⌋,⌊md⌋)μ(z)∑x=1⌊nd⌋∑y=1⌊md⌋(xy)d[z∣gcd(x,y)]\sum_{d=1}^{min(n,m)}d^d\sum_{z=1}^{min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(z)\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}(xy)^{d}[z|gcd(x,y)]d=1min(n,m)ddz=1min(dn,dm)μ(z)x=1dny=1dm(xy)d[zgcd(x,y)]
x,yx,yx,y的枚举替换成枚举zzz的倍数
∑d=1min(n,m)dd∑z=1min(⌊nd⌋,⌊md⌋)μ(z)∑t1=1⌊ndz⌋∑t2=1⌊mdz⌋(z2t1t2)d=∑d=1min(n,m)dd∑z=1min(⌊nd⌋,⌊md⌋)z2dμ(z)∑t1=1⌊ndz⌋t1d∑t2=1⌊mdz⌋t2d\sum_{d=1}^{min(n,m)}d^d\sum_{z=1}^{min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(z)\sum_{t_1=1}^{\lfloor\frac{n}{dz}\rfloor}\sum_{t_2=1}^{\lfloor\frac{m}{dz}\rfloor}(z^2t_1t_2)^{d}=\sum_{d=1}^{min(n,m)}d^d\sum_{z=1}^{min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}z^{2d}\mu(z)\sum_{t_1=1}^{\lfloor\frac{n}{dz}\rfloor}t_1^d\sum_{t_2=1}^{\lfloor\frac{m}{dz}\rfloor}t_2^{d}d=1min(n,m)ddz=1min(dn,dm)μ(z)t1=1dznt2=1dzm(z2t1t2)d=d=1min(n,m)ddz=1min(dn,dm)z2dμ(z)t1=1dznt1dt2=1dzmt2d

虽然由于每一项几乎都有d次幂不方便维护前缀和
但认真观察发现最后那两个和式每次从上一次d-1次幂继承,只用O(m/d)O(m/d)O(m/d)处理
z2dμ(z)z^{2d}\mu(z)z2dμ(z)需要O(n/d∗logd)O(n/d*logd)O(n/dlogd)处理
总复杂度是O(nlogn)O(nlogn)O(nlogn)级的

由于每个测试点只有一组数据,完全可以接受


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;
#define lowbit(x) ((x)&(-x))
 
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const int mod=1000000007;
const int maxn=500010;
int T;
int miu[maxn];
int prim[maxn],vis[maxn],cnt;
lt sum[maxn],a[maxn];
 
lt qpow(lt a,int k)
{
    lt res=1;
    while(k>0){
        if(k&1) res=(res*a)%mod;
        a=(a*a)%mod; k>>=1;
    }
    return res;
}
 
void Miu(int n)
{
    miu[1]=1;
    for(int i=2;i<=n;++i)
    {
        if(!vis[i]) prim[++cnt]=i,miu[i]=-1;
        for(int j=1;j<=cnt;++j)
        {
            if(i*prim[j]>n) break;
            vis[i*prim[j]]=1;
            if(i%prim[j]==0) break;
            else miu[i*prim[j]]=-miu[i];
        }
    }
}
 
lt query(int n,int m)
{
    lt res=0; if(n>m) swap(n,m);
    for(int i=1;i<=m;++i) a[i]=1;
     
    for(int d=1;d<=n;++d)
    {
        for(lt i=1;i<=m/d;++i)
        {
            a[i]=a[i]*i%mod;
            sum[i]=(sum[i-1]+a[i])%mod;
        }
         
        lt tt=0;
        for(int j=1;j<=n/d;++j)
        {
            tt+=(qpow(j,2*d)*miu[j]%mod+mod)%mod * sum[n/(j*d)]%mod * sum[m/(j*d)]%mod;
            tt%=mod;
        }
         
        res+=qpow(d,d)*tt%mod; 
        res%=mod;
    }
    return res;
}
 
int main() 
{
    int n=read(),m=read();
    Miu(min(n,m));
    printf("%lld",query(n,m));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值