NEFU 163 Co-prime(质数分解+容斥)


Description

<span style="font-size:18px;">Given a number N, you are asked to count the number of integers between A and B
inclusive which are relatively prime to N.Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than 1 or, equivalently, if their greatest common divisor is 1.The number 1 is relatively prime to every integer.</span>

Input

<span style="font-size:18px;">The first line on input contains T (0 &amp;lt; T &amp;lt;= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 &amp;lt;= A &amp;lt;= B &amp;lt;= 10^15) and (1 &amp;lt;=N &amp;lt;= 10^9).</span>

Output

<span style="font-size:18px;">For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.</span>

Sample Input

<span style="font-size:18px;">2
1 10 2
3 15 5</span>

Sample Output

<span style="font-size:18px;">Case #1: 5
Case #2: 10</span>

Hint

Source













分隔符

题意:

输入三个数:A,B,N,求在A到B之间(包含A,B)有多少个数与N互质。

思路:

类似这道题点击打开链接,首先对N质数分解,求出小于等于A中的数中与N互质的数的个数,再求出小于等于B中的数中与N互质的数的个数,两个数一减,再特判A,就行了。

求小于等于A中的数中与N互质的数的个数用到容斥,把N分解后的质因子一个一个去掉,去多的加回来,用一个dfs枚举每一种情况(2^9左右),奇偶次加减不一样具体分析就可以了。



#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
typedef long long LL;
LL Stack[1000],top,Cnt[1000],tmp[1000];
LL ans1,ans2,kk,tt,aa;
void fenjie(LL k)///素数分解
{
    top=1;
    memset(Cnt,0,sizeof(Cnt));
    for(LL i=2;i*i<=k;i++)if(k%i==0){
        while(k%i==0)Cnt[top]++,k/=i;
        Stack[top++]=i;
    }
    if(k>1){
        Cnt[top]++;
        Stack[top++]=k;
    }
}
LL q[11];
LL p[11];
void dfs(LL Stack[],int top,int s,int e,int k,LL q[],LL aa,LL &ans1)///dfs遍历质因数的所有情况,进行容斥
{
    q[k]=Stack[s];
    if(k==e)
    {
        if(e%2==1)
        {
            LL te=1;
            for(LL i=1;i<=e;i++)
            te*=q[i];
            LL temp=aa/te;
            ans1+=temp;///奇数次加
        }
        else
        {
            LL te=1;
            for(LL i=1;i<=e;i++)
            te*=q[i];
            LL temp=aa/te;
                ans1-=temp;///偶数次减
        }
    }
    else
    {
        for(int i=1;i+s<top;i++)
        {
            k++;
            dfs(Stack,top,s+i,e,k,q,aa,ans1);
            k--;
        }
    }
}
LL gcd(LL a,LL b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    int n;
    int cas=1;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%lld%lld%lld",&tt,&aa,&kk);
        fenjie(kk);

        ans1=0,ans2=0;
         for(int e=1;e<top;e++)
        for(int i=1;i<top;i++)
            {dfs(Stack,top,i,e,1,q,aa,ans2);
                dfs(Stack,top,i,e,1,p,tt,ans1);}
        LL anss=aa-tt+1+ans1-ans2;
        if(gcd(tt,kk)!=1)anss--;
        printf("Case #%d: %lld\n",cas++,anss);
    }
    return 0;
}


二进制枚举做法:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MOD=1e9+7;
typedef long long LL;
LL Stack[1000],top,Cnt[1000],tmp[1000];
LL ans,kk;
void fenjie(LL k)///素数分解
{
    top=0;
    memset(Cnt,0,sizeof(Cnt));
    for(LL i=2;i*i<=k;i++)if(k%i==0){
        while(k%i==0)Cnt[top]++,k/=i;
        Stack[top++]=i;
    }
    if(k>1){
        Cnt[top]++;
        Stack[top++]=k;
    }
}
LL solve(LL a)
{
    LL ans1=0;
    for(LL i=1;i<(1<<top);i++)
        {
            LL sum=0,ans=1;
            for(LL j=0;j<top;j++)
                if(i&(1<<j))
            {
                sum++;
                ans*=Stack[j];
            }
        if(sum&1)
            ans1+=a/ans;
        else
            ans1-=a/ans;
        }
        return ans1;
}
int main()
{
    int n;
    scanf("%d",&n);
    LL A,B,N;
    int cas=1;
    while(n--)
    {
        scanf("%lld%lld%lld",&A,&B,&N);
        fenjie(N);
        LL ans=B-A+1-solve(B)+solve(A-1);
        //cout<<solve(A-1)<<" "<<solve(B)<<endl;
        printf("Case #%d: %lld\n",cas++,ans);

    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值