题意:求(1,b)区间和(1,d)区间里面gcd(x, y) = k的数的对数(1<=x<=b , 1<= y <= d)。
解法1:b和d分别除以k之后的区间里面,只需要求gcd(x, y) = 1就可以了,这样子求出的数的对数不变。这道题目还要求1-3 和 3-1 这种情况算成一种,因此只需要限制x小于y就可以了,只需要枚举x,然后确定另一个区间里面有多少个y就可以了。因此问题转化成为区间(1, d)里面与x互素的数的个数。先求出x的所有质因数,因此(1,d)区间里面是x的质因数倍数的数都不会与x互素,因此,只需要求出这些数的个数,减掉就可以了。如果w是x的素因子,则(1,d)中是w倍数的数共有d/w个。
容斥原理: 所有不与x互素的数的个数= 1个因子倍数的个数 - 2个因子乘积的倍数的个数 + 3个……
复杂度O(nlogn)我记得PO爷在他的莫比乌斯反演的论文中就提到过这种nlogn的方法,那个时候还没想明白,orz太弱啦

//HDU 1695 312ms
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
vector <int> pri[maxn];
void pre_init() //存每一个数的质因数
{
    for(int i = 2; i < 100010; i++){
        int now = i;
        for(int j = 2; j*j <= now; j++){
            if(now % j == 0){
                pri[i].push_back(j);
                while(now%j == 0) now /= j;
            }
            if(now == 1) break;
        }
        if(now > 1) pri[i].push_back(now);
    }
}
int cal(int x, int tot)
{
    int ans = 0;
    for(int i = 1; i < (1<<pri[x].size()); i++){
        int num = 0;
        int tmp = 1;
        for(int j = 0; j < (int)pri[x].size(); j++){
            if((i>>j)&1){
                num++;
                tmp *= pri[x][j];
            }
        }
        if(num%2==1) ans += tot/tmp;
        else ans -= tot/tmp;
    }
    return tot - ans;
}

int main()
{
    pre_init();
    int t, ks = 0;
    scanf("%d", &t);
    while(t--){
        int a, b, c, d, k;
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
        if(k == 0){
            printf("Case %d: 0\n", ++ks);
            continue;
        }
        b /= k;
        d /= k;
        if(b < d) swap(b, d);
        long long ans = 0;
        for(int i = 1; i <= b; i++){
            ans += cal(i, min(i, d));
        }
        printf("Case %d: %lld\n", ++ks, ans);
    }
    return 0;
}

解法2:裸的莫比乌斯反演,但是这里统计方案的时候需要把重复的去掉。
复杂度是O(n^0.5)

//HDU 1695 0ms
#include <bits/stdc++.h>
using namespace std;
using namespace std;
const int maxn = 2e5+10;

int vis[maxn];
int prime[maxn];
int cnt;
int mu[maxn];
int sum[maxn];

void init()
{
    memset(vis,0,sizeof(vis));
    cnt=0;
    mu[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!vis[i])
        {
            prime[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt&&i*prime[j]<maxn;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j])
                mu[i*prime[j]]=-mu[i];
            else
            {
                mu[i*prime[j]]=0;
                break;
            }
        }
    }
    sum[0]=0;
    for(int i=1;i<maxn;i++)
        sum[i]=sum[i-1]+mu[i];
}

int main()
{
    int a,b,c,d,k;
    init();
    int T,ca=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        printf("Case %d: ",ca++);
        if(k==0)
        {
            printf("0\n");
            continue;
        }
        b=b/k;
        d=d/k;
        if(b>d)
            swap(b,d);
        long long ans1=0;
        int last;
        for(int i=1;i<=b;i=last+1)
        {
            last=min(b/(b/i),d/(d/i));
            ans1+=(long long)(sum[last]-sum[i-1])*(b/i)*(d/i);
        }
        long long ans2=0;
        for(int i=1;i<=b;i=last+1)
        {
            last=b/(b/i);
            ans2+=(long long)(sum[last]-sum[i-1])*(b/i)*(b/i);
        }
        long long ans=ans1-ans2/2;
        printf("%lld\n",ans);
    }
    return 0;
}