HDU 1695 GCD

题目链接:GCD

解题思路:一开始搜莫比乌斯反演的时候找到的,结果不会,后来看discuss里面说容斥原理+欧拉函数,那么要求gcd(x, y) = k就可以转化为gcd(x / k, y / k) = 1,那么就是互素的数队。欧拉函数表示的就是小于n并且和n互素的数。所以对于区间[1,a][1, b](a < b)来说, 小于a的部分就是直接加欧拉函数值就可以了,那么大于a小于等于b的部分就要容斥原理。设Q是这个部分的一个数,那么对于区间[1,a],只要里面的数不包含Q的质因数就互素,所以我们要找出来这些数。

设Q = I * J * K 我们要减去[1,a]内I, J, K的倍数,就是(a / I + a / J + a / k),发现里面有些数减多了,就是IJ的倍数,JK的倍数,IK的倍数,所以再加回来,又发现加多了,再减去IJK的倍数。所以我们观察发现由偶数个不同的质因数就要减去,奇数个就要加上,这就是容斥原理


RE原因,一直没看K可以等于0,除0报错

TLE原因,对于每一组数据来说都要重新加一遍欧拉函数和,并且对于每一个数都要sqrt(n)来分解质因数,太慢了,所以就预先打出表来。

WA原因,我之前计算的话总是莫名其妙少1,发现我少算了(1,1) 这个点,我就天真的加了1,一直WA,其实应该把euler[1] = 1,这样就对了,但是我的程序一直都没有计算过(1,1)并且所有数据除了k=0特判过了,其他的都必须有(1,1),所以不知为什么。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll __int64
#define MAX 100100

using namespace std;

bool isPrime[MAX];
int prime[MAX], eulur[MAX], cnt;
vector<int> p[MAX];
ll falei[MAX];
ll ans;
int fa[50], tt;

void init(){
    ans = 0;
}

void Eulur(){
    int i, j, k;
    falei[1] = eulur[1] = 1;
    for(i = 2; i < MAX; i++){
        if(!isPrime[i]){
            prime[cnt++] = i;
            eulur[i] = i - 1;
            p[i].push_back(i);
        }
        for(j = 0; j < cnt && i * prime[j] < MAX; j++){
            isPrime[i * prime[j]] = true;
            if(i % prime[j] == 0){
                eulur[i * prime[j]] = eulur[i] * prime[j];
                for(k = 0; k < p[i].size(); k++){
                    p[i * prime[j]].push_back(p[i][k]);
                }
                break;
            }
            else{
                eulur[i * prime[j]] = eulur[i] * (prime[j] - 1);
                for(k = 0; k < p[i].size(); k++){
                    p[i * prime[j]].push_back(p[i][k]);
                }
                p[i * prime[j]].push_back(prime[j]);
            }
        }
        falei[i] = falei[i - 1] + eulur[i];
    }
    eulur[1] = 1;
}

int get(int x, int y){
    int i, j, k, ret = 0;
    for(i = 1; i < (1 << p[x].size()); i++){
        int tmp = i, sum = 1, amt = 0;
        j = 0;
        while(tmp){
            if(tmp & 1){
                sum *= p[x][j];
                amt++;
            }
            tmp >>= 1;
            j++;
        }
        if(sum <= y && sum != 0){
            ret += y / sum * (amt % 2 ? 1 : -1);
        }
    }
    return y - ret;
}


int main(){
    int i, j, k;
    int a, b, c, d, e;
    int t, cas = 1;
    Eulur();
    scanf("%d", &t);
    while(t--){
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &e);
        init();
        if(e == 0){
            printf("Case %d: %d\n", cas++, 0);
            continue;
        }
        b /= e, d /= e;
        a = min(b, d), c = max(b, d);
        ans += falei[a];
        for(i = a + 1; i <= c; i++){
            ans += get(i, a);
        }
        printf("Case %d: %I64d\n", cas++, ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值