bzoj2154: Crash的数字表格/2693: jzptab [莫比乌斯反演、数论推导]

公式编辑器不会用,只能从认识的神犇空间里copy一下。

考虑求解:

那么

再来考虑求最终答案:

事实上到了这一步之后就可以求解2154了,在求F(n,m)中分块,在求Ans(n,m)中也分块处理,这样的时间复杂度是O(sqrt(n)*sqrt(n))=O(n),用线性筛法求u(d)d^2(u(d)表示莫比乌斯函数),总复杂度仍然是O(n),但是这样没法做到快速支持多组查询,所以还要继续化简:

然后我们惊奇的发现H(D)是可以用线性筛顺便求出,那么对D分块,处理出一个H(D)的前缀和,这样就可以在O(sqrt(n))的时间内处理出一个询问了,所以总复杂度是O(n)-O(sqrt(n)),这样就可以解决2693啦。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看完后的感受:


个人觉得这才是莫比乌斯反演的精髓所在,G(n,m,d)是什么并不重要,它依不同题目变化不同,但是F(n,m)这样的反演求法,可以保证i,j互质。

很多和gcd和lcm有关的题目都可以转化为这样的模型。

其余部分,都是些无关莫比乌斯反演的数论推导,倒是没有那么难理解。


我们要做的,就是找到一个奇怪的像H一样的积性函数,并在欧拉筛中对其进行预处理。


2154

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define For(i,l,r) for(ll (i)=(l);(i)<=(r);(i)++)
using namespace std;
  
typedef long long ll;
  
const ll N=10100000;
const ll T=2;
const ll MOD=20101009;
  
ll pr[N],H[N],A[T][2],P[N],tot=0,lim=0;
bool np[N];
  
void Pre(){
    memset(np,0,sizeof(np));
    H[1]=1; np[1]=1;
    For(i,2,lim){
        if(!np[i]){
            H[i]=(MOD-(((ll)i*i)%MOD)+i)%MOD;
            pr[++tot]=i;
        }for(ll j=1;j<=tot&&i*pr[j]<=lim;j++){
            np[i*pr[j]]=1;
            if(i%pr[j]==0){
                H[i*pr[j]]=(ll)pr[j]*H[i]%MOD;
                break;
            }else H[i*pr[j]]=(ll)H[i]*H[pr[j]]%MOD;
        }
    }P[0]=0; For(i,1,lim){
        P[i]=(P[i-1]+H[i])%MOD;
    }
}
  
inline ll S( ll n , ll m ) {
        return ((n*(n+1)/ll(2))%MOD)*((m*(m+1)/ll(2))%MOD)%MOD;
}
inline ll solve( ll n , ll m ) {
        ll ans=0,pos,v;
        if (n>m) swap(n,m) ;
        for(ll i=1;i<=n;i=pos+1){
                pos=min(n/(n/i),m/(m/i));
                v=((P[pos]-P[i-1]+MOD)%MOD*S(n/i,m/i))%MOD;
                (ans+=v)%=MOD;
        }return ll(ans) ;
}
  
  
int main(){
    int t=1;
    For(i,1,t){
        scanf("%lld%lld",&A[i][0],&A[i][1]);
        lim=max(lim,max(A[i][0],A[i][1]));
    }Pre();
    For(i,1,t) printf("%lld\n",solve(A[i][0],A[i][1])%MOD);  
    return 0;
} 


2693

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define For(i,l,r) for(ll (i)=(l);(i)<=(r);(i)++)
using namespace std;
  
typedef long long ll;
  
const ll N=10100000;
const ll T=10010;
const ll MOD=100000009;
  
ll pr[N],H[N],A[T][2],P[N],tot=0,lim=0;
bool np[N];
  
void Pre(){
    memset(np,0,sizeof(np));
    H[1]=1; np[1]=1;
    For(i,2,lim){
        if(!np[i]){
            H[i]=(MOD-(((ll)i*i)%MOD)+i)%MOD;
            pr[++tot]=i;
        }for(ll j=1;j<=tot&&i*pr[j]<=lim;j++){
            np[i*pr[j]]=1;
            if(i%pr[j]==0){
                H[i*pr[j]]=(ll)pr[j]*H[i]%MOD;
                break;
            }else H[i*pr[j]]=(ll)H[i]*H[pr[j]]%MOD;
        }
    }P[0]=0; For(i,1,lim){
        P[i]=(P[i-1]+H[i])%MOD;
    }
}
  
inline ll S( ll n , ll m ) {
        return ((n*(n+1)/ll(2))%MOD)*((m*(m+1)/ll(2))%MOD)%MOD;
}
inline ll solve( ll n , ll m ) {
        ll ans=0,pos,v;
        if (n>m) swap(n,m);
        for(ll i=1;i<=n;i=pos+1){
                pos=min(n/(n/i),m/(m/i));
                v=((P[pos]-P[i-1]+MOD)%MOD*S(n/i,m/i))%MOD;
                (ans+=v)%=MOD;
        }return ll(ans) ;
}
  
  
int main(){ 
    int t;
    scanf("%d",&t);
    For(i,1,t){
        scanf("%lld%lld",&A[i][0],&A[i][1]);
        lim=max(lim,max(A[i][0],A[i][1]));
    }Pre();
    For(i,1,t) printf("%lld\n",solve(A[i][0],A[i][1])%MOD);  
    return 0;
} 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值