莫比乌斯反演进阶-洛谷P2257/HDU5663

学了莫比乌斯反演之后对初阶问题没有任何问题了,除法分块也码到飞起,但是稍微变形我就跪了。用瞪眼观察法观察别人题解观察到主要内容除了柿子变形之外,主要就是对于miu函数的操作求前缀和。进而了解miu函数,miu函数是在这个数是否有平方因子的个数,每次推的套路是先用欧拉筛筛出来所有需要的函数,然后用每次需要用到的函数进行累计迭代加到前缀和,二次过筛,然后堆起来前缀和,用除法分块就行了,这个方法屡试不爽。

两道题,一道是洛谷P2257 YY的GCD

这道题求的是1-n和1-m区间内gcd为质数的个数,对此我们有暴力O(nmtlogn)算法,用莫比乌斯反演之后,我们枚举质数,每次来一遍分块,可以降到O(t * sqrtn + n),显然对于1e7这种级别的,质数都有66w个,复杂度显而易见是不够优秀的,那么我们需要一些加速,就是用前缀和处理出来这个区间内有质数gcd的数目,然后跑分块就可行了,复杂度根号

#include <bits/stdc++.h>
using namespace std;
#define limit (10000000 + 5)//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-6
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a,b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\dabiao.txt", "wt", stdout)
#define debug(x) cout<<x<<endl
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
    ll sign = 1, x = 0;char s = getchar();
    while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();}
    while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();}
    return x * sign;
}//快读
void write(ll x){
    if(x < 0) putchar('-'),x = -x;
    if(x / 10) write(x / 10);
    putchar(x % 10 + '0');
}
int prime[limit],tot,num[limit],miu[limit];
ll sum[limit];
void get_prime(const int &n = 1e7){
    memset(num,1,sizeof(num));
    num[1] = num[0] = 0;
    miu[1] = 1;
    rep(i,2,n){
        if(num[i])prime[++tot] = i,miu[i] = -1;
        for(int j = 1; j <= tot && prime[j] * i <= n ; ++j){
            num[prime[j] * i] = 0;
            if(i % prime[j] == 0){
                miu[i * prime[j]] = 0;
                break;
            }else{
                miu[i * prime[j]] = -miu[i];//莫比乌斯函数
            }
        }
    }
    rep(i ,1,tot){
        for(int j = prime[i] ; j <= n ; j += prime[i]){
            sum[j] += miu[j / prime[i]];//几个平方因子
        }
    }
    rep(i ,1,n){
        sum[i] += sum[i-1];
    }

}//素数筛
ll n,m;
const ll d = 1;
ll calc(){
    ll ans = 0;
    for(int l = 1,r ; l <= min(n/d,m/d); l = r + 1){
        //值域分块
        ll t = n / d , s = m / d;
        r = min(t / (t / l), s / (s / l));
        ans += (sum[r] - sum[l - 1]) * (t / l) * (s / l);
    }

    return ans;
}
int main() {
#ifdef LOCAL
    FOPEN;
#endif
    get_prime(10000000);
    int kase = read();
    while (kase--){
        n = read(), m = read();
        write(calc()),putchar('\n');
    }
    return 0;
}
AC Code

 

吐槽一下卡常,这卡得什么玩意儿,快读快写都上吸了氧才ac,吐了吐了

然后是HDU 5663

 

这题一看就特别熟悉,直接处理出是平方数的μ前缀和,然后跑一边莫比乌斯反演就行

#include <bits/stdc++.h>
using namespace std;
#define limit (10000000 + 5)//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-6
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a,b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\dabiao.txt", "wt", stdout)
#define debug(x) cout<<x<<endl
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
    ll sign = 1, x = 0;char s = getchar();
    while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();}
    while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();}
    return x * sign;
}//快读
void write(ll x){
    if(x < 0) putchar('-'),x = -x;
    if(x / 10) write(x / 10);
    putchar(x % 10 + '0');
}
int prime[limit],tot,num[limit],miu[limit];
ll sum[limit];
void get_prime(const int &n = 1e7){
    memset(num,1,sizeof(num));
    num[1] = num[0] = 0;
    miu[1] = 1;
    rep(i,2,n){
        if(num[i])prime[++tot] = i,miu[i] = -1;
        for(int j = 1; j <= tot && prime[j] * i <= n ; ++j){
            num[prime[j] * i] = 0;
            if(i % prime[j] == 0){
                miu[i * prime[j]] = 0;
                break;
            }else{
                miu[i * prime[j]] = -miu[i];//莫比乌斯函数
            }
        }
    }
    for(int i = 1 ; i * i <= n ; ++i){
        for(int j = i * i ; j <= n ; j += i * i){
            sum[j] += miu[j / i / i];//记录所有的平方
        }
    }
    rep(i,2,n){
        sum[i] += sum[i-1];
    }
}//素数筛
ll n,m;
const ll d = 1;
ll calc(){
    ll ans = 0;
    for(int l = 1,r ; l <= min(n/d,m/d); l = r + 1){
        //值域分块
        ll t = n / d , s = m / d;
        r = min(t / (t / l), s / (s / l));
        ans += (sum[r] - sum[l - 1]) * (t / l) * (s / l);
    }

    return ans;
}
int main() {
#ifdef LOCAL
    FOPEN;
#endif
    get_prime(1e7 + 1);
    int kase = read();
    rep(ka,1,kase){
        n = read(), m = read();
        printf("%lld\n",n * m - calc());
    }
    return 0;
}
AC Code

嘿哈

posted @ 2020-08-21 14:31  tiany7  阅读(157)  评论(0编辑  收藏  举报