二进制与容斥原理

Co-prime

Time Limit 1000ms

Memory Limit 65536K

description

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.

input

The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 10^15) and (1 <=N <= 10^9).

output

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.

sample_input

2
1 10 2
3 15 5

sample_output

Case #1: 5
Case #2: 10

用二进制位每一位 0 还是 1
来表示取还是不取, 因为一个 10^9 的数个素因子的个数据我目测不会超过 16, 直接枚举就
行了

我的代码:


Accepted
0k1ms

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define INT long long
using namespace std;
const int maxn =1000000000;
int pn;
INT b,d,n;
int p[100];
int ppn;
void fun(INT k, INT &ans) {
    ans = 1;
    int i;
    int cnt = 0;
    for (i = 0; i < ppn && k > 0; k >>= 1, i++) {
        if (k & 1) {
            ans *=p[i];
            cnt++;
        }
    }
    if (!(cnt & 1)) ans = -ans; //偶数取反在这里取了!!
}
void calc() {
    INT i, j;
    ppn = 0;
    //分解质因数
    for (i = 2; i*i <= n; i++) {
        if (n % i == 0) {
            p[ppn++] =i;
            while (n % i == 0) n /= i;
        }
    }
    if (n != 1) p[ppn++] = n;
    INT sum = 0;
    INT tmp;
    for (i = 1; i < (1 << ppn); i++) {
        fun(i, tmp); //按二进制计算指定素数的最小公倍数
        tmp = (b-1) / tmp; //计算可以被这个数整除的数字个数
        sum += tmp;
    }
    b=(b-1)-sum;
    sum=0;
    for (i = 1; i < (1 << ppn); i++) {
        fun(i, tmp); //按二进制计算指定素数的最小公倍数
        tmp = d / tmp; //计算可以被这个数整除的数字个数
        sum += tmp;
    }
    d-=sum; //取反
}
int main() {


    int T, tt;
    cin >> T;
    for (tt = 1; tt <= T; tt++) {
        scanf("%lld%lld%lld", &b, &d, &n);
        if (b > d) swap(b, d);
        calc();
        printf("Case #%d: %lld\n", tt, d-b);
    }
    return 0;
}

利用vector 写的容斥原理:明显比上一个慢


Accepted
0k3msC++

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;


ll solve(ll n,ll r)
{
    vector<int>p;
    p.clear();
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            p.push_back(i);
            while(n%i==0)
            {
                n/=i;
            }
        }
    }
    if(n>1)
    p.push_back(n);
    ll sum=0;
    for(ll j=1;j<(1LL<<p.size());j++)
    {
        ll mul=1,bit=0;
       for(ll k=0;k<p.size();k++)
       {
           if(j&(1LL<<k))
           {
               bit++;mul*=p[k];
           }
       }
    ll num=r/mul;
    if(bit&1)
    sum+=num;
    else
    sum-=num;
    }
    return r-sum;
}


int main() {


    int T, tt;
    ll b,d,n;
    cin >> T;
    for (tt = 1; tt <= T; tt++) {
        scanf("%lld%lld%lld", &b, &d, &n);
        if (b > d) swap(b, d);
        ll ans1=solve(n,d);
        ll ans2=solve(n,b-1);
        printf("Case #%d: %lld\n", tt, ans1-ans2);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值