hdu 1695 GCD (数论,容斥)

题意:

给出两个区间,求这个两个区间这样的一对数:(a,b) gcd(a,b)=k 的个数,(a,b)和(b,a)等价。

题解:

要求区间(1,l)(1,r)一对数满足最大公倍数等于k,那么等价于求(1,l/k)(1,r/k)一对数满足互质。继续分析发现可以将问题分成两个部分解决:我们假设l比r更小

1、(1,l)和(1,l)这个区间的某个数i,其中一部分解就是这些i对应在(1,i)区间互质的数的个数和。

2、(1,l)和(l,r)求第二个区间的每个数i在第一个区间中互质的数的个数。

第一部分的解计算方法可以用欧拉函数求解。第二部分的解用容斥解决。

对于第二部分解求法:

对于区间(l,r)某个数i,要求互质的数的个数,我们可以先考虑逆问题,我们先求不互质的数的个数,那么不互质的数肯定满足是这个数i质因子的倍数,那么根据容斥原理去枚举子因子,用dfs求解。然后累加所有的i对应互质数的个数。

注意:k可以等于0

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
void cmax(int& a,int b){ if(b>a)a=b; }
void cmin(int& a,int b){ if(b<a)a=b; }
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const int MOD=1000007;
const int maxn=100005;
ll euler[maxn];
int p[maxn][15];
int num[maxn];

void Init(){
    euler[1]=1;
    for(int i=2;i<maxn;i++){
        if(!euler[i]){
            for(int j=i;j<maxn;j+=i){
                if(!euler[j]) euler[j]=j;
                euler[j]=euler[j]*(i-1)/i;
                p[j][num[j]++]=i;
            }
        }
        euler[i]+=euler[i-1];
    }
}

ll dfs(int cnt,int x,int a){
    ll res=0;
    for(int i=cnt;i<num[a];i++){
        res+=x/p[a][i]-dfs(i+1,x/p[a][i],a);
    }
    return res;
}

int main(){
    //freopen("E:\\read.txt","r",stdin);
    int a,b,k,T,l,r;
    ll ans;
    Init();
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        scanf("%d%d%d%d%d",&a,&a,&b,&b,&k);
        if(k==0){ printf("Case %d: 0\n",cas);continue; }
        l=min(a,b);
        r=max(a,b);
        l/=k;
        r/=k;
        ans=euler[l];
        for(int i=l+1;i<=r;i++){
            ans+=l-dfs(0,l,i);
        }
        printf("Case %d: %I64d\n",cas,ans);
    }
    return 0;
}





你可能感兴趣的:(hdu 1695 GCD (数论,容斥))