51NOD 1222 最小公倍数计数 [莫比乌斯反演 杜教筛]

1222 最小公倍数计数

题意:求有多少数对\((a,b):a<b\)满足\(lcm(a,b) \in [1, n]\)

\(n \le 10^{11}\)


卡内存!

枚举\(gcd, \frac{a}{gcd}, \frac{b}{gcd}\),然后\(\mu\)代入,就是
\[ \sum_{d=1}^{\sqrt{n}}\mu(d) \sum_i \sum_j \sum_k [ijk \le \frac{n}{d^2}] \]
问题就是怎么求后面的式子了

一开始我是
\[ f(n) = \sum_i \sum_j \sum_k [ijk \le n] = \sum_{i=1}^n g(i) \\ g(n) = \sum_{i=1}^n \sigma(i) \]
杜教筛\(g\),方法是卷上\(\mu\),当然还要杜教筛\(\sum\mu\),但不影响复杂度,还是\(O(n^{\frac{2}{3}})\)

本机6.5s,改小预处理的大小后当然T了

然后又用分块的方法算\(g\),预处理前\(O(n^{2/3})\)\(\sigma\)剩下的分块\(O({\sqrt{n}})\)计算,复杂度也是\(O(n^{\frac{2}{3}})\)

本机4.6s,改小预处理大小又T了...

最后还是用了tangjz的方法,统计\(abc \le n\)的数对个数,规定\(a\le b \le c\),然后a枚举\(n^{\frac{1}{3}}\),b枚举\(\sqrt{\frac{n}{a}}\)。最后乘上\(3!\)再减去\(a= b <c,\ a<b = c,\ a=b=c\)

这一部分的复杂度\(T'(n)=O(\sum_{i=1}^{n^\frac{1}{3}}{\sqrt{\frac{n}{i}}})=O(n^\frac{2}{3})\)

总体复杂度\(T(n)=O(\sum_{d=1}^{\sqrt{n}}{T'(\frac{n}{d^2})})=?O(n^\frac{2}{3})​\)

求积分好像是\(\sqrt{n}\)啊,我也不知道这是怎么算的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long ll;
const int N=320000;
int U=316230;
inline ll read(){
    char c=getchar(); ll x=0,f=1;
    while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    return x*f;
}

bool notp[N]; int p[N], mu[N];
void sieve(int n) {
    mu[1]=1; 
    for(int i=2; i<=n; i++) {
        if(!notp[i]) p[++p[0]] = i, mu[i] = -1;
        for(int j=1; j <= p[0] && i*p[j] <= n; j++) {
            notp[ i*p[j] ] = 1;
            if(i % p[j] == 0) {mu[ i*p[j] ] = 0; break;}
            mu[ i*p[j] ] = -mu[i];
        }
    }
}

inline ll cal(ll n) {
    ll ans=0;
    for(ll i=1; i*i*i <= n; i++) {
        for(ll j=i; i*j*j <= n; j++) {
            ans += (n/i/j - j + 1) * 6;
            if(i == j) ans -= (n/i/j - j) * 3;
            if(i != j) ans -= 3;
        }
        ans -= 5;
    }
    return ans;
}

ll solve(ll n) {
    ll ans=0;
    int m = sqrt(n);
    for(int i=1; i<=m; i++) if(mu[i]) ans += mu[i]>0 ? cal(n/i/i) : -cal(n/i/i);
    return (ans + n) / 2;
}
int main() {
    freopen("in", "r", stdin);
    sieve(U);
    ll l=read(), r=read();
    printf("%lld", solve(r) - solve(l-1));
    printf("\n\n%lf\n",(double)clock()/CLOCKS_PER_SEC);
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <ctime>
using namespace std;
typedef long long ll;
const int N=22000005;
int U=22000000;
inline ll read(){
    char c=getchar(); ll x=0,f=1;
    while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    return x*f;
}

bool notp[N]; int p[N/10], mu[N], lp[N]; ll si[N];
void sieve(int n) {
    mu[1]=1; si[1]=1;
    for(int i=2; i<=n; i++) {
        if(!notp[i]) p[++p[0]] = i, mu[i] = -1, si[i] = lp[i] = 2;
        for(int j=1; j <= p[0] && i*p[j] <= n; j++) {
            int t = i*p[j];
            notp[t] = 1;
            if(i%p[j] == 0) {
                mu[t] = 0;
                lp[t] = lp[i] + 1;
                si[t] = si[i] / lp[i] * lp[t];
                break;
            }
            mu[t] = -mu[i];
            lp[t] = 2;
            si[t] = si[i] * 2;
        }
        mu[i] += mu[i-1];
        si[i] += si[i-1];
    }
}

namespace ha {
    const int p = 1001001;
    struct ha {
        struct meow{int ne; ll val, r;} e[10000];
        int cnt, h[p];
        ha() {cnt=0; memset(h, 0, sizeof(h));}
        inline void insert(ll x, ll val) {
            int u = x%p;
            for(int i=h[u];i;i=e[i].ne) if(e[i].r == x) return;
            e[++cnt] = (meow){h[u], val, x}; h[u] = cnt;
        }
        inline ll quer(ll x) {
            int u = x%p;
            for(int i=h[u];i;i=e[i].ne) if(e[i].r == x) return e[i].val;
            return -1;
        }
    } hs, hu, me; 
} using ha::hs; using ha::hu; using ha::me;

ll dj_u(ll n) { //return 1;
    if(n <= U) return mu[n];
    if(hu.quer(n) != -1) return hu.quer(n);
    ll ans = 1, r;
    for(ll i=2; i<=n; i=r+1) {
        r = n/(n/i);
        ans -= (r-i+1) * dj_u(n/i);
    }
    hu.insert(n, ans);
    return ans;
}

ll dj_s(ll n) {
    if(n <= U) return si[n];
    if(hs.quer(n) != -1) return hs.quer(n);
    dj_u(n);
    ll ans = n, r, now, last = dj_u(1);
    for(ll i=2; i<=n; i=r+1, last=now) {
        r = n/(n/i); now = dj_u(r);
        ans -= (now - last) * dj_s(n/i);
    }
    hs.insert(n, ans);
    return ans;
}
ll cal(ll n) { 
    ll ans=0, r;
    for(ll i=1; i<=n; i=r+1) {
        r = n/(n/i);
        ans += (r-i+1) * dj_s(n/i);  
    }
    return ans;
}
ll solve(ll n) { 
    ll ans=0;
    int m = sqrt(n);
    for(ll i=1; i<=m; i++) if(mu[i] - mu[i-1]) ans += (mu[i] - mu[i-1]) * cal(n / (i*i));
    return (ans + n) / 2;
}
ll l, r;
int main() {
    freopen("in", "r", stdin);
    sieve(U);
    l=read(); r=read();
    //printf("%lld", dj_u(r));
    printf("%lld", solve(r) - solve(l-1));
    printf("\n\n%lf\n",(double)clock()/CLOCKS_PER_SEC);
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long ll;
const int N=22000005;
int U=22000000;
inline ll read(){
    char c=getchar(); ll x=0,f=1;
    while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    return x*f;
}

bool notp[N]; int p[N/10], mu[N], lp[N]; ll si[N];
void sieve(int n) {
    mu[1]=1; si[1]=1;
    for(int i=2; i<=n; i++) {
        if(!notp[i]) p[++p[0]] = i, mu[i] = -1, si[i] = lp[i] = 2;
        for(int j=1; j <= p[0] && i*p[j] <= n; j++) {
            int t = i*p[j];
            notp[t] = 1;
            if(i%p[j] == 0) {
                mu[t] = 0;
                lp[t] = lp[i] + 1;
                si[t] = si[i] / lp[i] * lp[t];
                break;
            }
            mu[t] = -mu[i];
            lp[t] = 2;
            si[t] = si[i] * 2;
        }
        //mu[i] += mu[i-1];
        si[i] += si[i-1];
    }
}
inline ll g(ll n) { if(n <= U) return si[n];
    ll ans=0, r;
    for(ll i=1; i<=n; i=r+1) {
        r = n/(n/i);
        ans += (r-i+1) * (n/i);
    }
    return ans;
}
ll cal(ll n) {
    ll ans=0, r;
    for(ll i=1; i<=n; i=r+1) {
        r = n/(n/i);
        ans += (r-i+1) * g(n/i); 
    }
    return ans;
}

ll solve(ll n) { 
    ll ans = 0;
    int m = sqrt(n);
    for(ll i=1; i<=m; i++) 
        if(mu[i]) ans += mu[i] * cal(n / (i*i));
    return (ans + n) / 2;
}

ll l, r;
int main() {
    freopen("in", "r", stdin);
    sieve(U);
    l=read(); r=read();
    printf("%lld", solve(r) - solve(l-1));
    printf("\n\n%lf\n",(double)clock()/CLOCKS_PER_SEC);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值