Codeforces 235 E Number Challenge(莫比乌斯反演)

题目链接:
Codeforces 235 E Number Challenge
题意:

d(i)ii=1aj=1bk=1cd(ijk)a,b,c[1,2000]

分析:
Ans = i=1aj=1bk=1cd(ijk) = i=1aaij=1bbjk=1cckgcd(i,j)=gcd(i,k)=gcd(j,k)=1

对于后面的部分反演可得:
Ans=i=1i=aaidmin(b,c)μ(d)d|j,(i,j)=1bjd|k,(i,k)=1ck

j=dj',k=dk',j=dj',k=dk',
Ans=i=1i=aaidmin(b,c)μ(d)gcd(i,dj)=1bdjgcd(i,dk)=1cdk
,
因为 gcd(i,dj)=1 ,我们先保证 gcd(i,d)=1, 然后枚举 j:1bd ,保证 gcd(i,j)=1 ,这样就可以使得 gcd(i,dj)=1 ,累加即可。对于 gcd(i,dk)=1 同样处理。
枚举 i d的时间复杂度是是 a min(b,c),但是枚举 jk 的时间复杂度是 i=bi=1b/i i=ci=1c/i ,所以总的时间复杂度大概是 O(ablog(b))

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <bitset>
using namespace std;
typedef long long ll;
const int MAX_N = 2010;
const ll mod = (ll)1 << 30;

int prime_cnt, prime[MAX_N], mu[MAX_N], gcd[MAX_N][MAX_N];
bitset<MAX_N> bs;

void GetMu()
{
    prime_cnt = 0;
    bs.set();
    mu[1] = 1;
    for(int i = 2; i < MAX_N; ++i) {
        if(bs[i]) {
            prime[prime_cnt++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < prime_cnt && i * prime[j] < MAX_N; ++j) {
            bs[i * prime[j]] = 0;
            if(i % prime[j]) {
                mu[ i * prime[j]] = - mu[i];
            }else {
                mu[i * prime[j]] = 0;
                break;
            }
        }   
    }
}

inline int GCD(int a, int b)
{
    if(gcd[a][b]) return gcd[a][b];
    else return b == 0 ? a : gcd[a][b] = GCD(b, a % b);
}

inline void GetGcd()
{
    for(int i = 1; i < MAX_N; i++){
        for(int j = i; j < MAX_N; j++) {
            gcd[i][j] = gcd[j][i] = GCD(i, j);
        }
    }
}

inline ll work(int n, int x)
{
    ll res = 0;
    for(int i = 1; i <= n; ++i) {
        if(gcd[i][x] == 1) {
            res = (res + (n / i)) % mod;
        }
    }
    return res;
}

inline ll solve(int a, int b, int c)
{
    ll res = 0;
    int top = min(b, c);
    for(int i = 1; i <= a; ++i) {
        for(int d = 1; d <= top; ++d){
            if(gcd[i][d] == 1) {
                ll tmp = 0;
                tmp = (ll) (a / i) * mu[d] * work(b / d, i) % mod * work(c / d, i) % mod;
                res = ((res + tmp) % mod + mod) % mod;
            }
        }
    }
    return res;
}

int main()
{
    GetMu();
    GetGcd();
    int a, b, c;
    while(~scanf("%d%d%d", &a, &b, &c)) {
        printf("%lld\n", solve(a, b, c));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值