zoukankan      html  css  js  c++  java
  • 51nod

    https://www.51nod.com/Challenge/Problem.html#!#problemId=1363
    (sumlimits_{i=1}^{n}lcm(i,n))


    先换成gcd:
    (sumlimits_{i=1}^{n}frac{i*n}{gcd(i,n)})

    显而易见,枚举g:
    $ n * sumlimits_{g|n} frac{1}{g} sumlimits_{i=1}^{n} i*[gcd(i,n)==g] $

    提g,没有下整符号:
    $ n * sumlimits_{g|n} frac{1}{g} * g sumlimits_{i=1}^{frac{n}{g}} i*[gcd(i,frac{n}{g})==1] $


    考虑子问题:
    $sumlimits_{i=1}^{n} i*[gcd(i,n)==1] $

    也就是n以内和n互质的数的和。
    显然可以枚举因数d把他们减掉:
    $sumlimits_{i=1}^{n} i + sumlimits_{d|n,d!=1}mu(d)(d+2d+3d+...+n) $

    显然前面那堆等于 (d==1) 的结果。
    $sumlimits_{d|n}mu(d)(d+2d+3d+...+n) $

    比如求36的互质数的和时,6在枚举2和枚举3的时候算重,要加回去,4在枚举2的时候算过,不用算。
    化简:
    $ sumlimits_{d|n}mu(d)d(1+2+3+...+frac{n}{d}) ( )sumlimits_{d|n}mu(d)dfrac{(1+frac{n}{d})*frac{n}{d}}{2} $

    即:
    $frac{n}{2}sumlimits_{d|n}mu(d)(1+frac{n}{d}) ( 里面分配率: )frac{n}{2}(sumlimits_{d|n}mu(d)+sumlimits_{d|n}mu(d)frac{n}{d}) $

    然后一换:
    (frac{n}{2}([n==1]+varphi(n) ))

    求这个东西的复杂度很显然,预处理因子的欧拉函数是根号的,求单个n的欧拉函数也是根号的,所以整个式子就是根号的。

    记这个子问题为 (p(n)=sumlimits_{i=1}^{n} i*[gcd(i,n)==1] = frac{n}{2}([n==1]+varphi(n) )),求解它的复杂度就来源于欧拉函数


    回到$ n * sumlimits_{g|n} p(frac{n}{g}) $

    非常明显根号以内的欧拉函数可以重复利用,然后单独求一个最大的。

    总体复杂度是根号的。理论上根号是过不了的,1.7e8左右,但是看别人搞什么质因数分解?这个不也是最坏根号的吗?我觉得他们能行我也能行。

    果断T了,质因数分解假如遇到合数应该是跑得比我这快得多的。


    $ n * sumlimits_{g|n} p(frac{n}{g}) = frac{n}{2} (sumlimits_{g|n} gvarphi(g))+frac{n}{2}$

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    inline ll read() {
        ll x=0;
        char c=getchar();
        while(c<'0'||c>'9')
            c=getchar();
        do {
            x=(x<<3)+(x<<1)+c-'0';
            c=getchar();
        } while(c>='0'&&c<='9');
        return x;
    }
    
    inline void write(ll x) {
        //printf("%lld
    ",x);
        if(x>9) {
            write(x/10);
        }
        putchar(x%10+'0');
        return;
    }
    
    const int MAXN=5e6;
    //有用的质数恰好有3402个
    const int mod=1e9+7;
    const int inv2=mod+1>>1;
    
    int pri[MAXN+1];
    int &pritop=pri[0];
    int phi[MAXN+1];
    
    void sieve(int n=MAXN) {
        phi[1]=1;
        for(int i=2; i<=n; i++) {
            if(!phi[i]) {
                pri[++pritop]=i;
                phi[i]=i-1;
            }
            for(int j=1; j<=pritop&&i*pri[j]<=n; j++) {
                int t=i*pri[j];
                if(i%pri[j]) {
                    phi[t]=phi[i]*phi[pri[j]];
                } else {
                    phi[t]=phi[i]*pri[j];
                    break;
                }
            }
        }
    }
    
    inline int get_phi_pk(int n,int p){
        int res=n-n/p;
        return res;
    }
    
    int get_phi(int n){
        int res=n;
        for(int i=1;i<=3402;i++){
            if(n%pri[i]==0){
                res=res/pri[i]*(pri[i]-1);
                while(n%pri[i]==0)
                    n/=pri[i];
            }
            if(n==1)
                return res;
        }
        res=res/n*(n-1);
        return res;
    }
    
    int p(int n){
        ll res=(n==1);
        if(n<=MAXN)
            res+=phi[n];
        else
            res+=get_phi(n);
        res=(1ll*n*res)>>1;
        if(res>=mod)
            res%=mod;
        //printf("p[%d]=%lld
    ",n,res);
        return res;
    }
    
    int fac[80][2];
    int ftop=1;
    int q(int n){
        int cn=n;
        ll res=1;
        for(int i=1;pri[i]*pri[i]<=n;i++){
            if(n%pri[i]==0){
                ll tmpsum=1;
                ll pk=1;
                fac[ftop][0]=pri[i];
                fac[ftop][1]=0;
                while(n%pri[i]==0){
                    pk*=pri[i];
                    if(pk>=MAXN)
                        tmpsum+=pk*get_phi_pk(pk,pri[i]);
                    else
                        tmpsum+=pk*phi[pk];
                    fac[ftop][1]++;
                    n/=pri[i];
                }
                tmpsum%=mod;
                res*=tmpsum;
                res%=mod;
            }
        }
        if(n!=1){
            ll tmpsum=1+1ll*n*(n-1);
            res*=tmpsum;
            res%=mod;
        }
        //printf("q[%d]=%lld
    ",cn,res);
        return res;
    }
    
    ll ans(int n){
        ll res=(q(n)+1);
        res*=n;
        res%=mod;
        res*=inv2;
        res%=mod;
        return res;
    }
    
    inline void solve() {
        sieve();
        int t=read();
        while(t--){
            int n=read();
            write(ans(n));
            putchar('
    ');
        }
    }
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in","r",stdin);
    #endif // Yinku
        solve();
        return 0;
    }
    

    上面这个有很多多余操作,比如我现在根本就不关心欧拉函数怎么求了,也没必要给他们初始化这么多了,把要用的质数筛出来就结束了。欧拉函数在pk位置的求法就是(varphi(p^k)=p^k-(p^k)/p=(p-1)varphi(p^{k-1}))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    inline int read() {
        int x=0;
        char c=getchar();
        while(c<'0'||c>'9')
            c=getchar();
        do {
            x=(x<<3)+(x<<1)+c-'0';
            c=getchar();
        } while(c>='0'&&c<='9');
        return x;
    }
    
    inline void write(int x) {
        //printf("%lld
    ",x);
        if(x>9) {
            write(x/10);
        }
        putchar(x%10+'0');
        return;
    }
    
    const int MAXN=4e5;
    const int mod=1e9+7;
    const int inv2=mod+1>>1;
    
    int pri[MAXN+1];
    int &pritop=pri[0];
    int phi[MAXN+1];
    
    void sieve(int n=MAXN) {
        phi[1]=1;
        for(int i=2; i<=n; i++) {
            if(!phi[i]) {
                pri[++pritop]=i;
                phi[i]=i-1;
            }
            for(int j=1; j<=pritop&&i*pri[j]<=n; j++) {
                int t=i*pri[j];
                if(i%pri[j]) {
                    phi[t]=phi[i]*phi[pri[j]];
                } else {
                    phi[t]=phi[i]*pri[j];
                    break;
                }
            }
        }
    }
    
    int q(int n){
        int cn=n;
        ll res=1;
        for(int i=1;pri[i]*pri[i]<=n;i++){
            if(n%pri[i]==0){
                ll tmpsum=1;
                ll pk=1;
                while(n%pri[i]==0){
                    pk*=pri[i];
                    tmpsum+=pk*(pk-pk/pri[i]);
                    n/=pri[i];
                }
                if(tmpsum>=mod)
                    tmpsum%=mod;
                res*=tmpsum;
                if(res>=mod)
                    res%=mod;
            }
        }
        if(n!=1){
            ll tmpsum=1ll*n*(n-1)+1;
            res*=tmpsum;
            if(res>=mod)
                res%=mod;
            res%=mod;
        }
        //printf("q[%d]=%lld
    ",cn,res);
        return res;
    }
    
    int ans(int n){
        ll res=q(n)+1;
        res*=n;
        if(res>=mod)
            res%=mod;
        res*=inv2;
        if(res>=mod)
            res%=mod;
        return res;
    }
    
    inline void solve() {
        sieve();
        int t=read();
        while(t--){
            int n=read();
            write(ans(n));
            putchar('
    ');
        }
    }
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in","r",stdin);
    #endif // Yinku
        solve();
        return 0;
    }
    
  • 相关阅读:
    Movement Type
    Excel制表技巧
    [转]MM移动类型的配置相关的系统表,举例说明SAP是如何根据配置抛帐的
    Microsoft Excel:Automatically color alternating rows(columns)
    eBook试载(1):Workbook对象与示例(1)
    辨析:would rather 和 prefer
    Serial Number Management In SAP SD
    <摘录>NAL格式
    linux服务简介关闭不需要的Linux服务
    <摘录>PS和TS流的区别
  • 原文地址:https://www.cnblogs.com/Yinku/p/10987912.html
Copyright © 2011-2022 走看看