Problem(莫比乌斯反演)

我不是传送门

题意 : 中文题目不解释

求gcd(x,y) = k (a<=x<=b, c<=y<=d);

根据gcd(ka,kb) = k*gcd(a,b), 可将问题转化为求gcd(a/k, b/k) = 1;

再由容斥定理可得到gcd(x,y) = gcd(b,d)- gcd(a,d)- gcd(c,b)+ gcd(a,c);

再套上莫比乌斯反演的模板, 嗯, 然后就能得到一次TE;

正解 : 容斥+莫比乌斯反演+分块优化;

分块优化 : 考虑到[n/i]、[m/i]都会有大量的完全相等的部分,我们可以把[n/i]、[m/i]都相等的部分放在一起算,也就是一个分块的思想。预处理出μ(d)的前缀和即可。

参考链接

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;
#define ll long long
const int maxn = 50005;


ll pri[maxn], mu[maxn];
ll vis[maxn];

void init()
{
    memset(vis, 0, sizeof(vis));
    memset(mu, 0, sizeof(mu));
    mu[1] = 1;
    int cnt = 0;
    for(ll i =2; i <= 50005; i++)
    {
        if(vis[i] == 0) {mu[i] = -1; pri[cnt++] = i;}
        for(ll j =0; j < cnt&&i*pri[j] <= 50005; j++)
        {
            ll k = i*pri[j];
            vis[k] = 1;
            if(i%pri[j] == 0) {mu[k] = 0; break; }
            else mu[k] = -mu[i];
        }
    }
    for(ll i = 2; i <= 50005; i++) // 前缀和处理mu;
        mu[i] += mu[i-1];
}

ll cal(ll l, ll r)  // 分块优化
{
    if(l > r) swap(l, r);
    ll ans = 0;
    ll hay = 0;
    for(ll i = 1; i <=l; i = hay+1)
    {
        hay = min(l/(l/i), r/(r/i) );
        ans += (mu[hay] - mu[i-1])*(l/i)*(r/i);
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    init();
    ll n;
    cin >> n;
    while(n--)
    {
        ll a, b, c, d, k;
        cin >> a >> b >> c >> d >> k;
        ll ans  = 0;
        ans =  cal(b/k, d/k)+ cal((a-1)/k,(c-1)/k) - cal((a-1)/k, d/k)- cal((c-1)/k, b/k);
        printf("%lld\n", ans);

    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/mrh-acmer/p/9458151.html
今日推荐