HDU - 4135 Co-prime(容斥+质因数分解)

该博客主要介绍了如何利用容斥原理和质因数分解解决编程问题,具体是计算[l,r]区间内与给定数n互质的数的个数。博主通过分析最大公约数的意义,提出了动态规划和位运算的解决方案,通过枚举质因数的状态来避免重复计算,并给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

传送门


题目大意

[ l , r ] [l,r] [l,r]中和 n n n互质的数的个数

解题思路

根据容斥定理问题可以转化为 [ 1 , x ] [1,x] [1,x]中和 n n n互质的个数,然后 s o l v e ( r ) − s o l v e ( l − 1 ) solve(r)-solve(l-1) solve(r)solve(l1)即可。但是互质的数不好求,问题又变成了求出不互质的数的个数,然后拿 n n n减去就得到了互质的个数。

x , y x,y x,y不互质等价于 g c d ( x , y ) ≠ 1 gcd(x,y) \neq 1 gcd(x,y)=1,考虑最大公约数在质因数分解下的意义,也就是考虑 n n n的每一种质数的组合,求出 n n n以内有多少个它的倍数。但是这样会重复计算。假设 n = 2 x ∗ 3 y n=2^x*3^y n=2x3y,我们已经统计了 n n n以内 2 2 2的倍数的个数 ⌊ n 2 ⌋ \lfloor \frac{n}{2} \rfloor 2n,然后再求 n n n以内 3 3 3的倍数的个数 ⌊ n 3 ⌋ \lfloor \frac{n}{3} \rfloor 3n,显然前者和后者有重复的统计部分,这个重复的部分就是 n n n以内 2 ∗ 3 2*3 23的倍数的个数,因此我们考虑所有的组合状态,若素数种类数为奇数就加上统计的部分,否则减去统计的部分。

代码

不难得知 i n t int int范围内最多有 9 9 9种质因数,即最大且种类最多的数为 2 ∗ 3 ∗ 5 ∗ . . . ∗ 23 2*3*5*...*23 235...23,那么所有的状态数也只有 ( 1 < < 9 ) − 1 (1<<9)-1 (1<<9)1种,状压枚举即可。

//
// Created by Happig on 2020/10/8
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

vector<ll> prime;

void init(ll n) {
    prime.clear();
    for (ll i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            prime.push_back(i);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) prime.push_back(n);
}

ll solve(ll r) {
    ll ans = 0;
    for (ll s = 1; s < (1 << prime.size()); s++) {
        ll num = 1, cnt = 0;
        for (int i = 0; i < prime.size(); i++) {
            if (s & (1 << i)) {
                cnt++;
                num *= prime[i];
            }
        }
        ll res = r / num;
        if (cnt & 1) ans += res;
        else ans -= res;
    }
    return r - ans;
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, kase = 0;
    ll l, r, n;
    cin >> t;
    while (t--) {
        cin >> l >> r >> n;
        init(n);
        cout << "Case #" << ++kase << ": " << solve(r) - solve(l - 1) << ENDL;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值