BZOJ 2301: [HAOI2011]Problem b 2045: 双亲数 同BZOJ 1101 Zap 莫比乌斯反演 3倍经验

2301: [HAOI2011]Problem b

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

思路 :

  在我Zap的博客里有具体实现 2301就是相当于矩阵前缀和的那种处理, 把不满足条件的减掉就好辣!

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
const int N = 51000;
bool np[N];
int pr[N], tot, mu[N],  sum[N];
using namespace std;
void init() {
	mu[1]=1;
	sum[1]=1;
    for(int i=2;i<N;i++) {
        if(!np[i]) {
            pr[++tot]=i;
            mu[i] = -1;
        }
        for(int j=1;j<=tot&&i*pr[j]<N;j++) {
            np[i*pr[j]]=1;
            if(i % pr[j] == 0) {
                mu[i*pr[j]] = 0;
                break; 
            }
            mu[i*pr[j]]=-mu[i];
        }
        sum[i] = sum[i-1] + mu[i];
    }
}
long long work(int a,int b,int d) {
    a/=d;
    b/=d;
    if(a>b)swap(a,b);
    int lst = 0;
    long long ans=0;
    for(int p=1;p<=a;p=lst+1) {
        lst=min(a/(a/p),b/(b/p));
        ans+=(long long)(sum[lst]-sum[p-1])*(a/p)*(b/p);
    }
    return ans;
}
int main() {
    int t,a,b,c, d, k;
    init();
    //for(int i=1;i<=50000;i++) printf("%d\n",sum[i]);
    scanf("%d",&t);
    while(t--) {

        scanf("%d%d%d%d%d",&a,&b,&c, &d, &k);
        a--, c--;
        long long ans1 = work(b,d,k);
        ans1-= work(a, d, k);
        ans1-= work(b, c, k);
        ans1+= work(a, c, k);
        printf("%lld\n", ans1);
    }
}

猜你喜欢

转载自www.cnblogs.com/Tobichi/p/9247754.html