洛谷 P2522 [HAOI2011]Problem b(容斥定理+莫比乌斯反演)

传送门


题目大意

∑ i = a b ∑ j = c d [ g c d ( i , j ) = k ] \sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i,j)=k] i=abj=cd[gcd(i,j)=k]

根据容斥定理:

∑ i = a b ∑ j = c d f ( i , j ) = ∑ i = 1 b ∑ j = 1 d f ( i , j ) − ∑ i = 1 a − 1 ∑ j = 1 d f ( i , j ) − ∑ i = 1 b ∑ j = 1 c − 1 f ( i , j ) + ∑ i = 1 a − 1 ∑ j = 1 c − 1 f ( i , j ) \sum_{i=a}^{b}\sum_{j=c}^{d}f(i,j)=\sum_{i=1}^{b}\sum_{j=1}^{d}f(i,j)-\sum_{i=1}^{a-1}\sum_{j=1}^{d}f(i,j)-\sum_{i=1}^{b}\sum_{j=1}^{c-1}f(i,j)+\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}f(i,j) i=abj=cdf(i,j)=i=1bj=1df(i,j)i=1a1j=1df(i,j)i=1bj=1c1f(i,j)+i=1a1j=1c1f(i,j)

这个东西感悟一下就行了,一开始我也没想到

最后的问题变成了求 ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = k ] \sum_{i=1}^n\sum_{j=1}^m[(i,j)=k] i=1nj=1m[(i,j)=k]

解题思路

因为 g c d ( i , j ) = k ⇒ g c d ( ⌊ i k ⌋ , ⌊ j k ⌋ ) = 1 gcd(i,j)=k \Rightarrow gcd(\lfloor \frac{i}{k} \rfloor,\lfloor \frac{j}{k} \rfloor)=1 gcd(i,j)=kgcd(ki,kj)=1,那么在上述式子中只需在两个 ∑ \sum 处做一个小小的转化: f ( n , m ) = ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ [ ( i , j ) = 1 ] f(n,m)=\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}[(i,j)=1] f(n,m)=i=1knj=1km[(i,j)=1]

考虑到 ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = 1 ] \sum_{i=1}^n\sum_{j=1}^m[(i,j)=1] i=1nj=1m[(i,j)=1]的求解,思路如下:
根据 ∑ d ∣ n μ ( d ) = [ 1 n ] \sum_{d|n}\mu(d) = [\frac{1}{n}] dnμ(d)=[n1],得: ∑ i n ∑ j m ∑ d ∣ ( i , j ) μ ( d ) \sum_i^n\sum_j^m\sum_{d|(i,j)}\mu(d) injmd(i,j)μ(d)
显然 d d d的范围是 [ 1 , n ] [1,n] [1,n],然后从我们枚举 d ∈ [ 1 , n ] , d ∣ ( i , j ) d \in [1,n],d|(i,j) d[1,n],d(i,j)得到: f ( n , m ) = ∑ d = 1 n μ ( d ) ∑ i = 1 n [ d ∣ i ] ∑ j = 1 m [ d ∣ j ] f(n,m)=\sum_{d=1}^n\mu(d)\sum_{i=1}^n[d|i]\sum_{j=1}^m[d|j] f(n,m)=d=1nμ(d)i=1n[di]j=1m[dj]
因为 i , j i,j i,j都是 d d d的倍数,实际上问题变成了对于每个 d d d,在 [ 1 , n ] [1,n] [1,n] [ 1 , m ] [1,m] [1,m]出现了多少次,这里可以联系下整除分块,最后得到:
∑ d = 1 n μ ( d ) ⌊ n d ⌋ ⌊ m d ⌋ \sum_{d=1}^n \mu(d) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor d=1nμ(d)dndm

联系上面,最后可以得到: ∑ d = 1 ⌊ n k ⌋ μ ( d ) ⌊ ⌊ n k ⌋ d ⌋ ⌊ ⌊ m k ⌋ d ⌋ = ∑ d = 1 ⌊ n k ⌋ μ ( d ) ⌊ n k d ⌋ ⌊ m k d ⌋ \sum_{d=1}^{\lfloor \frac{n}{k} \rfloor} \mu(d) \lfloor \frac{\lfloor \frac{n}{k} \rfloor}{d} \rfloor \lfloor \frac{\lfloor \frac{m}{k} \rfloor}{d} \rfloor=\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor} \mu(d) \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor d=1knμ(d)dkndkm=d=1knμ(d)kdnkdm

代码

//
// Created by Happig on 2020/9/24
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 5e4 + 10;

int mu[maxn], k;
vector<int> prime;
bitset<maxn> vis;

void init() {
    mu[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!vis[i]) {
            prime.push_back(i);
            mu[i] = -1;
        }
        for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j]) {
                mu[i * prime[j]] = -mu[i];
            } else {
                mu[i * prime[j]] = 0;
                break;
            }
        }
        mu[i] += mu[i - 1];
    }
}

ll cal(int n, int m) {
    int up = min(n, m);
    ll ans = 0;
    for (int l = 1, r; l <= up; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        if (r > up) r = up;
        ans += 1LL * (mu[r] - mu[l - 1]) * (n / (k * l)) * (m / (k * l));
    }
    return ans;
}


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, a, b, c, d;
    init();
    cin >> t;
    while (t--) {
        cin >> a >> b >> c >> d >> k;
        cout << cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1, c - 1) << ENDL;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值