BZOJ 2301 莫比乌斯反演

题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。。

将一个询问拆成四个询问,原问题即为:
ni=1mi=1[gcd(i,j)=k]

f(k) gcd(i,j)=k 的方案数, F(k)=k|gcd(i,j) 的方案数。
f(k)=k|dμ(d/k)F(d)=>f(1)=dμ(d)n/kdm/kd
后面这部分预处理 μ 的前缀和,跳着计数,就可以做到单组询问 O((n)) ,总复杂度 O(nlogn)

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
namespace shai {
    const LL maxn = 55000;
    LL p[maxn], tag[maxn], cnt, mob[maxn];
    LL smob[maxn];
    void gao() {
        cnt = 0;
        mob[1] = 1;
        for(LL i = 2; i < maxn; i++) {
            if(!tag[i]) {
                p[cnt++] = i;
                mob[i] = -1;
            }
            for(LL j = 0; j < cnt && i * p[j] < maxn; j++) {
                tag[i*p[j]] = 1;
                if(i % p[j] == 0) {
                    mob[i*p[j]] = 0;
                    break;
                }
                mob[i*p[j]] = -mob[i];
            }
        }
        for(LL i = 1; i < maxn; i++)
            smob[i] = smob[i-1] + mob[i];
    }
}

namespace solver {
    LL t;
    LL a, b, c, d, k;
    LL cal(LL n, LL m) {
        n /= k;
        m /= k;
        LL res = 0;
        LL last = 0;
        for(LL i = 1; i <= min(n, m); i=last + 1) {
            last = min(n/(n/i), m/(m/i));
            res += (n/i)*(m/i) * (shai::smob[last] - shai::smob[i-1]);
        }
        return res;
    }
    void solve() {
        scanf("%lld", &t);
        for(LL _ = 0; _ < t; _++) {
            scanf("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k);
            printf("%lld\n", cal(b, d) + cal(a-1, c-1) - cal(b, c-1) - cal(a-1, d));
        }
    }
}

int main() {
    shai::gao();
    solver::solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值