zoukankan      html  css  js  c++  java
  • AcWing 890. 能被整除的数

    题目传送门

    一、理论知识

    韦恩图(又称文氏图)

    (1)两个圆相交求面积

    $large S=S_1+S_2-S_1cap S_2$

    (2)三个圆相交求面积

    $large S=S_1+S_2+S_3- S_1cap S_2 -S_2cap S_3 - S_1cap S_3 + S_1cap S_2 cap S_3$

    (3)四个圆相交求面积

    $large S=S_1+S_2 + S_3 + S_4 -S_1cap S_2 -S_1cap S_3 -S_1cap S_4 - S_2cap S_3 -S_2cap S_4 -S_3cap S_4 + S_1cap S_2 cap S_3 + S_1cap S_2 cap S_4+ S_2cap S_3 cap S_4 ++ S_1cap S_3 cap S_4 - S_1 cap S_2 cap S_3 cap S_4$

    上面,我们是用面积来考虑的问题,所以等式左边写的是(S),也可以用集合来考虑,以(3)个圆为例,那就是
    (|S_1 cup S_2 cup S_3| =|S_1|+|S_2|+|S_3|- |S_1cap S_2| -|S_2cap S_3| - |S_1cap S_3| + |S_1cap S_2 cap S_3|)

    其中(||)代表集合中的元素个数。

    (4)规律总结

    上面的求解过程,其实是在求 (C_n^1 - C_n^2+ ... + {(-1)}^{n-1}C_n^n)
    也就理解为从(n)个选择1个,减去从(n)中选择(2)个,加上从(n)中选择(3)个,减去从(n)中减去(4)个...,也可以记为奇数个元素的集合是加,偶数个的(指相交)的集合是减。

    (5)经典例题

    容斥原理有个经典题目:一个班每个人都有自己喜欢的科目,有(20)人喜欢数学,(10)人喜欢语文,(11)人喜欢英语,其中(3)人同时喜欢数学语文,(3)人同时喜欢语文英语,(4)人同时喜欢数学英语,(2)人都喜欢,问全班有多少人?

    根据容斥原理,就是(large S=S_1+S_2+S_3- S_1cap S_2 -S_2cap S_3 - S_1cap S_3 + S_1cap S_2 cap S_3)

    班级人数=(20+10+11-3-3-4+2)

    二、算法思路

    • 数学表达式
      比如三个质数是(2,3,5),那么:(S_2)就代表(2)的倍数集合,(S_3)就代表(3)的倍数集合,(S_5)就代表(5)的倍数集合,至少能被其中一个质数整除的就是:
      (|S_2 cup S_3 cup S_5| = |S_2| + |S_3| +|S_5| -|S_2 cap S_3| -|S_3 cap S_5| -|S_2 cap S_5|+|S_2 cap S_3 cap S_5|)

    • 如何计算(|S_p|)这样的表达式数值
      (|S_p|)就是计算小于等于(n)中能整除掉(p)的数字个数。它就是等于(lfloor frac{n}{p} floor),而(C++)在整数运算除法时,默认就是下取整,这点就不用再特意处理了。

    • 如何计算(|S_2 cap S_3|)这样的的表达式值
      题目给定的(p_i)都是质数,所以能被(2)整除,也能被(3)整除的数,肯定是能被(6)整除的数,所以就是 (|S_6|=|S_2 cap S_3|),这样,问题就转化成了问题(|S_p|),也就会求解了!

    • 怎么把这些子项目都罗列出来
      其实罗列的是(p[i])的所有组合方式!举个栗子: ({2},{3},{5},{2,3},{3,5},{2,5},{2,3,5})(7)种,也就是(2^3-1=7)种。
      这个是常用的小技巧了,二进制模拟
      QQ截图20210308155806.png

    三、C++ 代码

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int N = 20;
    int p[N];   //质数数组
    int n;      //整数n,表示1~n之间的整数
    int m;      //m个不同的质数
    int res;    //结果
    
    int main() {
        //优化输入
        ios::sync_with_stdio(false);
        cin >> n >> m;
    
        //读入m个质数
        for (int i = 0; i < m; i++) cin >> p[i];
        //二进制来模拟m个数字的 2^m种选法,注意,不能全不选,所以i=1开始
        //举个栗子,比如  2,3,5共3个数字,就是 001~111共7种选法
        for (int i = 1; i < 1 << m; i++) {
            int t = 1;                      //t代表当前选法中,选中质数的乘积,初始值是1
            int cnt = 0;                    //cnt当前选法里面包含几个1,奇数个1是+,偶数个1是-,所以需要记录
            bool flag = true;               //本轮结果是否有效
            for (int j = 0; j < m; j++)     //遍历二进制的每一位
                if (i >> j & 1) {           //判断j位二进制是不是1
                    //如果质数乘积大于n了,就不用再继续算了
                    if ((LL) t * p[j] > n) {
                        flag = false;
                        break;
                    }
                    t *= p[j]; //质数相乘
                    cnt++;
                }
    
            //容斥原理公式
            if (flag) {
                if (cnt % 2) res += n / t;  //奇数项加,C++的整数除法默认是下取整,不用再关心取整问题
                else res -= n / t;          //偶数项减
            }
        }
        //输出答案
        printf("%d", res);
        return 0;
    }
    
  • 相关阅读:
    mysql视图产生派生表无法优化案例
    根据.frm .ibd文件恢复表
    binlog内容时间乱序问题排查
    mysql官方的测试数据库employees超30万的数据,安装方法介绍
    数据库大量Waiting for table flush 状态SQL问题排查
    mysql搭建从库并配置ssl
    MySQL lOAD DATA详解
    redis eval
    aws-rds for mysql 5.7.34时间点恢复数据
    MySQL 如何处理监听连接的
  • 原文地址:https://www.cnblogs.com/littlehb/p/15389237.html
Copyright © 2011-2022 走看看