hdoj 1695 GCD

思路:由题意可知求gcd(i, j) = k 满足i属于区间[a,b],j属于区间[c, d]. 进而转化成求gcd(i/k, j/k) = 1 满足i属于区间[1, b/k],j属于区间[1, d/k].即求两区间内互素数的对数有多少。

欧拉函数可求出所有小于等于n并且与n互素的数的个数。假设b<d,两区间内所有小于等于b的互素对数可由前b项欧拉数获得,剩下的是求区间[b+1,d]与区间[1, b]互素的数的对数。利用容斥原理,对于区间[b+1,d]的每项元素j,假设都和区间[1, b]互素,在排除掉b中与j有相同因子的项数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>

using namespace std;

typedef __int64 lint;

const int MAX = 100009;

lint f[MAX],sum[MAX], tmp;
bool p[MAX]={0};
vector<int> v[MAX];

void init()
{
	int i, j;
	sum[1] = 1;
	for(i = 0; i < MAX; i++)
		f[i] = i;
	for(i = 2; i < MAX; i++)
	{
        if(p[i] == 0)
		{
			f[i] = i-1;
			v[i].push_back(i);
			for(j = i + i; j <= MAX; j += i)
			{
				p[j] = 1;
				f[j] *= i-1;
				f[j] /= i;
				v[j].push_back(i);
			}
		}
	}
    for (i = 2; i <= MAX; ++i)
        sum[i] = sum[i-1] + f[i];
}

void dfs(int k, int j, int b, int lcm, int mark) {
    if (k >= (int)v[j].size())
        return;
    int i;
    for (i = k; i < (int)v[j].size(); ++i) {
        if (v[j][i])
        tmp += mark*b/(v[j][i]*lcm);
        int t = v[j][i];
        dfs(i+1, j, b, t*lcm, -mark);
    }
}

int main()
{
    init();
    int i, j, ca, a, b, c, d, k;
    lint ans;
    scanf("%d", &ca);
    for (i = 1; i <= ca; ++i) {
        scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
        if (k == 0) {
            printf("Case %d: 0\n", i);
            continue;
        }
        if (b > d)
            swap(b, d);
        b /= k;
        d /= k;
        ans = sum[b];
        for (j = b+1; j <= d; ++j) {
            tmp = 0;
            dfs(0, j, b, 1, 1);
            ans += b - tmp;
        }
        printf("Case %d: %I64d\n", i, ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值