HDU 1695(欧拉函数+容斥原理)

题意:在[1,b]找一个x,在[1,d]之间找一个y,使得gcd(x,y)= k,其中b,d,k<10^5;k可能=0;并且(x,y)与(y,x)视作一种情况。求(x,y)的对数;

算法:我们假设x<y,(否则swap)一定存在一个n<m使得   x=nk,y=mk。并且n与m互质。所以转换成求[1,b/k]内选择一个数a和[1,d/k]内选择一个数b,有多少对(a,b)互质。

很简单把[1,,d/k]区间分为两段[1,b/k]和[b/k+1,d/k]对于前一段区间直接区间内欧拉函数累加和即可。对于后一段区间需要用到容斥原理求某个数与n个数有多少个是互质的。正难则反,求有多少个数与某个数不互质,b/k-这个个数即可。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 100009
typedef long long LL;
int prime[N],euler[N],vis[N],top;
int have[N];
LL sum[N];
int a,b,c,d,k;
void getEuler(){
    memset(vis,0,sizeof(vis));
    euler[1] = top = 1;
    for(int i = 2;i<N;i++){
        if(vis[i]==0){
            prime[top++] = i;
            euler[i] = i - 1;
        }
        for(int j = 1;j<top;j++){
            if((LL)i*prime[j]>=N) break;
            vis[i*prime[j]] = 1;
            if(i%prime[j]==0){
                euler[i*prime[j]] = euler[i]*prime[j];
                break;
            }
            else euler[i*prime[j]] = euler[i]*(prime[j]-1);
        }
    }
}
void setHave(int y){
    memset(have,0,sizeof(have));
    int top1 = 0;
    int num[10009];
    for(int x = 1;x<=y;x++){
        top1 = 0;
        int tem = x;
        for(int i = 1;i<top&&prime[i]*prime[i]<=tem;i++){
            if(tem%prime[i]==0) num[top1++] = prime[i];
            while(tem%prime[i]==0) tem/=prime[i];
        }
        if(tem!=1) num[top1++] = tem;
        for(int i = 1;i<(1<<top1);i++){
            tem = 1;
            for(int j = 0;j<top1;j++){
                if((i&(1<<j))>0) tem *= num[j];
            }
            have[tem]++;
        }
    }
}
int solve(int x){
    int top1 = 0,ans = 0;
    int num[10009];
    int tem = x;
    for(int i = 1;i<top&&prime[i]*prime[i]<=tem;i++) {
        if(tem%prime[i]==0) num[top1++] = prime[i];
        while(tem%prime[i]==0) tem/=prime[i];
    }
    if(tem!=1) num[top1++] = tem;
    int k = 0;
    for(int i = 1;i<(1<<top1);i++) {
        tem = 1;k = 0;
        for(int j = 0;j<top1;j++){
            if((i&(1<<j))>0){
                tem *= num[j];
                k++;
            }
        }
        if(k%2==1) ans += have[tem];
        else       ans -= have[tem];
    }
    return ans;
}
void init(){
    getEuler();
    sum[0] = 0;
    for(int i = 1;i<N;i++)
        sum[i] = sum[i-1] + euler[i];
}
int main(){
    int T,cas=1;
    init();
    //freopen("Test.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0){//这里需要判断k==0情况 错了一发
            printf("Case %d: 0\n",cas++);
            continue;
        }
        if(b>d) swap(b,d);
        LL ans = sum[b/k];//第一次提交这里没有用LL型 错了一发
        setHave(b/k);
        for(int i = b/k+1;i<=d/k;i++)
            ans += (b/k - solve(i));
        printf("Case %d: %I64d\n",cas++,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值