Problem b(BZOJ-2301/HAOI-2011)

Problem 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

思路:

 GCD(HDU-1695) 相似,但 x、y 不是从 1 开始,因此需要使用容斥原理来进行处理

题目实质是要求:res=\sum_{i=a}^b\sum_{j=c}^d[gcd(i,j)=k]

利用容斥定理来进行转换,设cal(N,M,k)=\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)=k]

那么有:res=cal(b,d,k)-cal(a-1,d,k)-cal(c-1,b,k)+cal(a-1,c-1,k)

因此,现在只需要考虑计算 cal(N,M,k)=\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)=k] 即可

设 f(d) 为满足 gcd(i,j)=d 的个数,即:f(d)=\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d]

设 g(n) 为满足 gcd(i,j)=d 的倍数的个数,即:g(n)=\sum_{n|k}f(k)=\left \lfloor \frac{N}{n} \right \rfloor \left \lfloor \frac{M}{n} \right \rfloor

可以看出,f(d) 与 g(n) 符合莫比乌斯反演的形式:g(n)=\sum_{n|d}f(d) \Leftrightarrow f(n)=\sum_{n|d}u(\frac{d}{n})g(d)

那么,对于 cal(N,M,k)=\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)=k] 进行化简

将 f(k) 代入,有:cal(N,M,k)=f(k)

根据莫比乌斯反演,有:cal(N,M,k)=\sum_{k|d}u(\frac{d}{k})g(d)=\sum_{k|d}u(\frac{d}{k}) \left \lfloor \frac{N}{d} \right \rfloor \left \lfloor \frac{M}{d} \right \rfloor

设枚举项 \frac{d}{k} 为 t

那么有 cal(N,M,k)=\sum_{i=1}^{min(N,M)}u(t) \left \lfloor \frac{N}{kt} \right \rfloor \left \lfloor \frac{M}{kt} \right \rfloor

为了优化时间,可以将 \left \lfloor \frac{N}{kt} \right \rfloor \left \lfloor \frac{M}{kt} \right \rfloor 的 k 提出去,即有:cal(N,M,k)=\sum_{i=1}^{min(\frac{N}{k},\frac{M}{k})}u(t) \left \lfloor \frac{N}{t} \right \rfloor \left \lfloor \frac{M}{t} \right \rfloor

此时的复杂度为 O(n),由于是多组查询,发现式子中有整除,利用整除分块求 mu 的前缀和,进行优化即可

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
const int MOD = 1E9+7;
const int N = 100000+5;
const int dx[] = {1,-1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
int mu[N];
int prime[N];
bool bprime[N];
int cnt;
LL sum[N];
void getMu(int n){//线性筛求莫比乌斯函数
    cnt=0;
    mu[1]=1;//根据定义,μ(1)=1
    memset(bprime,false,sizeof(bprime));

    for(int i=2;i<=n;i++){//求2~n的莫比乌斯函数
        if(!bprime[i]){
            prime[++cnt]=i;//存储质数
            mu[i]=-1;//i为质数时,μ(1)=-1
        }
        for(int j=1;j<=cnt&&i*prime[j]<=n;j++){//枚举i之前的素数个数
            bprime[i*prime[j]]=true;//不是质数
            if(i%prime[j])//i不是prime[j]的整数倍时,i*prime[j]就不会包含相同质因子
                mu[i*prime[j]]=-mu[i];//mu[k]=mu[i]*mu[prime[j]],因为prime[j]是质数,mu值为-1
            else{
                mu[i*prime[j]]=0;
                break;//留到后面再筛
            }
        }
    }

    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+mu[i];
}
LL cal(LL n,LL m,LL k){//计算a在[1,n],b在[1,m]中GCD(a,b)=1的个数
    n/=k;
    m/=k;
    LL minn=min(n,m);
    LL res=0;
    for(int left=1,right;left<=minn;left=right+1){
        right=min(n/(n/left),m/(m/left));
        res+=(1LL)*(n/left)*(m/left)*(sum[right]-sum[left-1]);
    }
    return res;
}

int main(){
    getMu(100000);

    int t;
    scanf("%d",&t);
    int Case=1;
    while(t--){
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);

        LL res=cal(b,d,k)-cal(a-1,d,k)-cal(c-1,b,k)+cal(a-1,c-1,k);
        printf("%lld\n",res);
    }
    return 0;
}

 

发布了1871 篇原创文章 · 获赞 702 · 访问量 195万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览