容斥gcd

Description

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

Input

第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

Output

共n行,每行一个整数表示满足要求的数对(x,y)的个数

Sample Input

2
2 5 1 5 1
1 5 1 5 2

Sample Output

14
3

Hint

100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

思路:

我们先求1<=i<=b,1<=j<=d这个范围内的gcd(x,y)=k的个数。

可以看我的另一篇:acwing215.破译密码题解(容斥原理+mobius函数)_yusen_123的博客-CSDN博客

当可以求出1<=i<=b,1<=j<=d这个范围内的gcd(x,y)=k的个数。

我们设1<=i<=b,1<=j<=d这个范围内的gcd(x,y)=k的个数为slove(b,d).

我们可以用solve(b,d)-solve(a,d)-solve(b,c)+solve(a,c)求出答案.

代码:

#define _CRT_SECURE_NO_WARNINGS 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL  long long
const int N = 5e4 + 100;
const long long  mod = 1e9 + 7;
#define  rep(i,a,b) for (int i = a; i <= b; i++) 
#define per(i, a, b) for(int  i=a;i>=b;i--)
int  mobuis[N], v[N],sum[N];
void getmobuis()
{
    rep(i, 1, N) mobuis[i] = 1;
    mobuis[1]=v[1] = 1;
    rep(i, 2, N)
    {
        if (v[i]) continue;
        mobuis[i] = -1;
        for (int j = 2 * i; j <= N; j += i)
        {
            if ((j / i) % i == 0) mobuis[j] = 0;
            else
                mobuis[j] *= -1;
            v[j] = 1;
        }
    }
}
int main()
{
    getmobuis();
    rep(i, 1, N) sum[i] += sum[i - 1] + mobuis[i];
    int n,a, b,c,d,k;
    cin >> n;
    while (n--)
    {
        scanf("%d%d%d%d%d", &a, &b,&c,&d,&k);
        a--;
        c--;
        LL cnt = 0;
        a = a / k;
        b = b / k;
        c = c / k;
        d = d / k;
        int x = min(b, d);
        for (int i = 1,j=1; i <=x; i = j + 1)
        {
            j = min(x, min(b / (b/ i), d/ (d / i)));
                cnt+= (sum[j] - sum[i - 1]) * (LL)(b / i) * (d / i);
        }
        x = min(a, c);
        for (int i = 1,j=1; i <= x; i = j + 1)
        {
            j = min(x, min(a / (a/ i), c/ (c/ i)));
            cnt += (sum[j] - sum[i - 1]) * (LL)(a / i) * (c / i);
        }
        x = min(a, d);
        for (int i = 1,j=1; i <= x; i = j + 1)
        {
            j = min(x, min(a / (a / i), d / (d/ i)));
            cnt -= (sum[j] - sum[i - 1]) * (LL)(d / i) * (a / i);
        }
        x = min(c, b);
        for (int i = 1,j=1; i <= x; i = j + 1)
        {
            j = min(x, min(c / (c / i), b / (b/ i)));
            cnt -= (sum[j] - sum[i - 1]) * (LL)(c / i) * (b / i);
        }
        printf("%lld\n", cnt);
    }
    return 0;
}

你可能感兴趣的:(数论,算法,数据结构)