HDU 4135-Co-prime(容斥求区间内与N互质的个数(队列||位运算))

题目地址:HDU 4135
题意:求[A,B]区间内与N互质的数的个数。
思路:我们可以用容斥计算出[1,B]之间和[1,A-1]之间的与N互质的数,然后相减即可。然后我们用一种快速的方法求出[1,X]之间的与N互质的数,首先我们求出N的质因子,[1,X]之间与N的质因子成倍数关系的数肯定与N不成互质关系。
Eg:X=12,N=30 ,N的质因子为2,3,5。
(2,4,6,8,10)->X/2 6个;
(3,6,9,12)->X/3 4个;
(5,10)->X/5 ;
然后根据容斥原理X/2+X/3+X/5-X/(2*3)-X/(2*5)-X/(3*5)+X/(2*3*5)。
方法一:

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <map>
#include <bitset>
using namespace std;
typedef __int64 LL;
const int inf=0x3f3f3f3f;
const double pi= acos(-1.0);
const double esp=1e-6;
using namespace std;
const int Maxn=2*1e5+10;
LL prime[Maxn];
LL sprime[Maxn];
bitset<Maxn>pri;
LL k,cnt;
void is_prime()
{
    pri.set();
    for(LL i=2; i<Maxn; i++) {
        if(pri[i]) {
            prime[k++]=i;
            for(LL j=i+i; j<Maxn; j+=i)
                pri[j]=0;
        }
    }
}
void Divide(LL n)//分解质因子
{
    cnt=0;
    LL t=(LL)sqrt(1.0*n);
    for(LL i=0; prime[i]<=t; i++) {
        if(n%prime[i]==0) {
            sprime[cnt++]=prime[i];
            while(n%prime[i]==0)
                n/=prime[i];
        }
    }
    if(n>1)
        sprime[cnt++]=n;
}
LL Ex(LL n)//容斥原理之队列实现
{
    LL q[Maxn];
    LL sum=0;
    LL t=1;
    q[0]=-1;
    for(LL i=0; i<cnt; i++) {
        LL x=t;
        for(LL j=0; j<x; j++){
            q[t]=q[j]*sprime[i]*(-1);
            t++;
        }
    }
    for(LL i=1; i<t; i++)
        sum+=n/q[i];
    return sum;
}
int main()
{
    int T;
    int icase=1;
    LL A,B,N;
    LL res;
    is_prime();
    scanf("%d",&T);
        while(T--) {
            scanf("%I64d %I64d %I64d",&A,&B,&N);
            Divide(N);
            res=(B-Ex(B))-(A-1-Ex(A-1));
            printf("Case #%d: ",icase++);
            printf("%I64d\n",res);
        }
    return 0;
}

方法二:
对于X=12,N=30来说,N的质因子为2,3,5。如果用”0”,”1”标记2,3,5。为”1”时需要,为”0”时不需要,那么,二进制数010,011,······,111,刚好对应上式中的2,3,5在除数中出现的情况。

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <map>
#include <bitset>
using namespace std;
typedef __int64 LL;
const int inf=0x3f3f3f3f;
const double pi= acos(-1.0);
const double esp=1e-6;
using namespace std;
const int Maxn=1e5+10;
LL prime[Maxn];
LL sprime[Maxn];
bitset<Maxn>pri;
LL k,cnt;
void is_prime()
{
    pri.set();
    for(LL i=2; i<Maxn; i++) {
        if(pri[i]) {
            prime[k++]=i;
            for(LL j=i+i; j<Maxn; j+=i)
                pri[j]=0;
        }
    }
}
void Divide(LL n)//分解质因子
{
    cnt=0;
    LL t=(LL)sqrt(1.0*n);
    for(LL i=0; prime[i]<=t; i++) {
        if(n%prime[i]==0) {
            sprime[cnt++]=prime[i];
            while(n%prime[i]==0)
                n/=prime[i];
        }
    }
    if(n>1)
        sprime[cnt++]=n;
}
/*
1<<n是将1向前推动n位,例如n=5,那么1<<5=100000,i<(1<<5)的话就是在[00001,11111]中选择
i&j是只有对应位都为1的时候才是1,例如101和001的结果为001.
*/
LL Ex(LL n)
{
    LL ans=0;
    LL tmp,flag;
    LL i,j;
    for(i=1;i<(LL)(1<<cnt);i++)
    {
        tmp=1;
        flag=0;
        for(j=0;j<cnt;j++)
            if(i&((LL)(1<<j))){//这个就可以代表用了第几个
                flag++;
                tmp*=sprime[j];
            }
        if(flag&1)
            ans+=n/tmp;
        else
            ans-=n/tmp;
    }
    return ans;
}
int main()
{
    int T;
    int icase=1;
    LL A,B,N;
    LL res;
    is_prime();
    scanf("%d",&T);
        while(T--) {
            scanf("%I64d %I64d %I64d",&A,&B,&N);
            Divide(N);
            res=(B-Ex(B))-(A-1-Ex(A-1));
            printf("Case #%d: ",icase++);
            printf("%I64d\n",res);
        }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rocky0429

一块也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值