• 牛客小白赛5 无关(relationship) 容斥原理(计算因子数的模板)


    链接:https://www.nowcoder.com/acm/contest/135/A
    来源:牛客网

    若一个集合A内所有的元素都不是正整数N的因数,则称N与集合A无关。

      给出一个含有k个元素的集合A={a1,a2,a3,...,ak},求区间[L,R]内与A无关的正整数的个数。
      保证A内的元素都是素数

    输入描述:

    输入数据共两行:

    第一行三个正整数L,R,k,意义如“题目描述”。

    第二行k个正整数,描述集合A,保证k个正整数两两不相同。

    输出描述:

    输出数据共一行:

    第一行一个正整数表示区间[L,R]内与集合A无关的正整数的个数

    示例1

    输入

    复制
    1 10 4
    2 3 5 7

    输出

    复制
    1
    示例2

    输入

    复制
    2 10 4
    2 3 5 7

    输出

    复制
    0

    说明

    对于30%的数据:1<=L<=R<=10^6

    对于100%的数据:1<=L<=R<=10^18,1<=k<=20,2<=ai<=100

    分析:直接计算无关的数,要枚举计算,量很大。所以考虑转化成计算有关的数,然后用总数减去有关的数就是无关的数
    如我们要计算l到r的无关的数,就等于r-l+1-(f(r)-f(l-1)),其中f(x)代表1到x的有关的数
    然后计算有关的数,考虑计算有关数的时候会出现像6这样既是2的倍数又是3的倍数的数,所以我们在计算的过程中要用容斥原理来去重
    这里我们考虑用二进制状态的方法来计算每个数的容斥
    last=(1<<k)-1,然后通过与(1<<j)(0<=j<k)进行二进制与运算来保证循环的过程中,我们依次计算了a[0],a[0]*a[1],...,a[0]*a[1]*...*a[k-2]*a[k-1]的倍数
    计算出这些数有多少个倍数后用容斥原理的奇加偶减计算最后的结果
    #include <map>
    #include <set>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define debug(a) cout << #a << " " << a << endl
    using namespace std;
    const int maxn = 25;
    const int mod = 1e9 + 7;
    typedef long long ll;
    ll a[maxn], k;
    ll solve( ll n ) {  //求1到n区间和集合a有关的数的个数
        if( n == 0 ) {  //特判一下1到0区间不存在,所以满足条件的数的个数当然是0
            return 0;
        }
        ll ans = 0, last = ( 1 << k ) - 1;
        //用二进制数的二进制位表示集合a里每个数的使用状态,1表示使用了,
        //0表示没使用,对应二进制的第一位对应的是第一个数的使用状态,
        //last表示的是k个数的最后一个枚举方式1..1,(k个1)
        for( ll sta = 1; sta <= last; sta ++ ) { //这里从状态从1枚举到last,刚好把k个数的全部状态枚举了一遍
            ll cnt = 0; //记录当前枚举的状态里使用的数的个数
            ll flag = 0;
            ll tmp = 1; //tmp存的是枚举到的数的最小公倍数
            for( ll j = 0; j < k; j ++ ) {
                if( sta&(1<<j) ) {  
                    cnt ++;         
                    tmp *= a[j];
                    if( tmp > n ) {
                        flag = 1;
                        break;
                    }
                }
            }
            if( flag ) {
                continue;
            }
              if( cnt&1 ) {  //根据容斥定理,求k个集合的并集的元素个数时是奇加偶减
                ans += n/tmp;//我们要求的是这cnt个数,假设是a2,a4,a5,要求这些数在1到n的公共倍数有多少个,
        //就要先求出这cnt个数的lcm然后n/lcm即可,这里题目保证了a数组每个数都是质数,所以cnt个数的乘积即是对应的lcm
            } else {
                ans -= n/tmp;
            }
        }
        return ans;
    }
    int main() {
        ll le, ri;
        while( cin >> le >> ri >> k ) {
            for( ll i = 0; i < k; i ++ ) {
                cin >> a[i];
            }
            cout << ri-le+1-(solve(ri)-solve(le-1)) << endl;
        }
        return 0;
    }
    

      

    彼时当年少,莫负好时光。
  • 相关阅读:
    如何使用GOOGLE高级搜索技巧
    你所认为的极限,可能只是别人眼中的起点
    飞机选座——附:东航320选坐攻略
    古诗词里,从初识到相爱到分离到重逢的漫长过程
    从零开始学摄影
    Python之运维
    Linux用户和组密令大全
    centos7 下安装生物信息软件的问题小总结
    VMware锁定文件失败开启模块diskearly的操作失败未能启动虚拟机
    linux 基本命令整理--转
  • 原文地址:https://www.cnblogs.com/l609929321/p/9389319.html
Copyright © 2020-2023  润新知