51nod - 1363 - 最小公倍数之和 - 数论

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


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

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

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


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

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

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

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

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

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

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

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


回到$ n * \sum\limits_{g|n} p(\frac{n}{g}) $

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

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

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


$ n * \sum\limits_{g|n} p(\frac{n}{g}) = \frac{n}{2} (\sum\limits_{g|n} g\varphi(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\n",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",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\n",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('\n');
    }
}

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\n",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\n",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('\n');
    }
}

int main() {
#ifdef Yinku
    freopen("Yinku.in","r",stdin);
#endif // Yinku
    solve();
    return 0;
}
posted @ 2019-06-07 11:57  韵意  阅读(303)  评论(0编辑  收藏  举报