zoukankan      html  css  js  c++  java
  • Bzoj2154 Crash的数字表格 乘法逆元+莫比乌斯反演(TLE)

    题意:求sigma{lcm(i,j)},1<=i<=n,1<=j<=m

    不妨令n<=m

    首先把lcm(i,j)转成i*j/gcd(i,j)

    正解不会...总之最后化出来的莫比乌斯反演式子并没有除法…

    本脑子有坑选手的做法:20101009是一个质数,而且n和m的范围小于20101009,这一定有其原因。经过仔细思考,我们发现这保证了每个1~n的数都有mod20101009意义下的乘法逆元。用inv[x]表示x的逆元,我们发现原先的式子等于sigma{inv[gcd(i,j)]*i*j},1<=i<=n,1<=j<=m

    于是我们枚举g=gcd(i,j)则原式等于sigma{inv[g]*H(g)},1<=g<=n

    H(g)=sigma{i*j},1<=i<=n,1<=j<=m,gcd(i,j)==g.

    定义h(g)= sigma{i*j},1<=i<=n,1<=j<=m,g|gcd(i,j),我们发现,h(g)可以方便地求出,且h(g)是H(g)的倍数和,这启发我们使用莫比乌斯反演,H(g)=sigma{mu(q/g)*h(q)},g|q,1<=q<=n接下来我们将式子变形为先枚举q,则原式=sigma{h(q)*sigma{inv[g]*mu(q/g),g|q}}1<=q<=n

    我们知道莫比乌斯函数和乘法逆元都是积性函数,积性函数的积,积性函数的约数和也是积性函数,这启发我们用线性筛预处理G(q)=sigma{inv[g]*mu(q/g),g|q}!接下来暴力枚举q即可。

    而1-n所有数的逆元也有线性的方法可以求出,

    综上,我们得到了一个时空复杂度均为O(n)的优越算法(大雾)。

    然而常数大,T得飞起….

    #include<cstdio>
    #include<ctime> 
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=10000005;const ll mod=20101009;
    int niyuan[maxn];
    bool flag[maxn];
    int prime[maxn/10],mu[maxn],f[maxn],h[maxn],g[maxn],tot;
    void linear(){
        niyuan[1]=1;f[1]=1;
    //    for(int i=2;i<maxn;++i)niyuan[i]=niyuan[mod%i]*niyuan[mod%i]%mod*(mod/i)%mod*(mod/i)%mod*i%mod;//常数已炸天,0.6s+ 
        int tmp;
        for(int i=2,last;i<maxn;i=last+1){
            last=mod/(mod/i);
            tmp=mod/i;tmp=tmp*1LL*tmp%mod;
            for(int j=i;j<=last&&j<maxn;++j)niyuan[j]=niyuan[mod%j]*1LL*niyuan[mod%j]%mod*tmp*j%mod;
        }   
        mu[1]=1;
        for(int i=2;i<maxn;++i){
            if(!flag[i]){
                prime[++tot]=i;mu[i]=-1;
                f[i]=mu[i]+niyuan[i];h[i]=i;g[i]=1;
            }
            for(int j=1;j<=tot&&prime[j]*i<maxn;++j){
                tmp=i*prime[j];
                flag[tmp]=true;
                if(i%prime[j]==0){
                    mu[tmp]=0;
                    g[tmp]=g[i];
                    h[tmp]=h[i]*1LL*prime[j]%mod;
                    f[tmp]=f[g[i]]*1LL*(niyuan[h[i]*1LL*prime[j]%mod]-niyuan[h[i]]+mod)%mod;
                    break;
                }else{
                    mu[tmp]=-mu[i];
                    h[tmp]=prime[j];
                    g[tmp]=i;
                    f[tmp]=f[i]*1LL*f[prime[j]]%mod;
                }
            }
        } 
    }
    int n,m;
    ll f2(ll x){
        ll a=n/x,b=m/x;
        return a*(a+1)%mod*b%mod*(b+1)%mod*niyuan[4]%mod*x*x%mod; 
    }
    int solve(){
        if(n>m)swap(n,m);
        int ans=0;
        for(int d=1;d<=n;++d){
            ans=ans+f2(d)*f[d]%mod;ans%=mod;
        }
        return ans;
    }
    int main(){
        linear();
        scanf("%d%d",&n,&m);
        printf("%d
    ",solve());
        return 0;
    }

     UPD:线性筛改成筛到min(n,m)而不是筛到maxn,因为小数据比较多,按总时限在bzoj卡过去了,感人肺腑(虽然极限数据的单点时限还是会T)

    (有个数组开longlong结果MLE了)

  • 相关阅读:
    Linux常用命令大全详解
    C++语言关键字及注解
    求两数的最大公约数
    ICOP完成端口详解
    C/C++常见面试题
    猴子吃桃问题之《C语言经典案例分析》
    DTD
    DTD
    DTD的使用
    Rust
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6160711.html
Copyright © 2011-2022 走看看