Acwing 容斥原理

news2024/12/28 15:44:17

容斥原理

先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理

以S1,S2,S3三个集合为例,求出三个集合中包含的元素个数,可以通过韦恩图得到S1∪S2∪S3 = S1+S2+S3- S1∩S2 - S1∩S3 - S2∩S3 + S1∩S2∩S3。通过数学归纳法可以证明,对于求n个集合S1,S2,…,Sn集合中包含的元素个数,可以通过下面的公式来计算(注意正负号交替):
∣ S 1 ∪ S 2 ⋯ ∪ S m ∣ = ∑ i ∣ S i ∣ − ∑ i , j ∣ S i ∩ S j ∣ + ∑ i , j , k ∣ S i ∩ S j ∩ S k ∣ . . . . + ( − 1 ) n − 1 ∑ ∣ S 1 ∩ S 2 ∩ S 3 . . . ∩ S m ∣ . . . . . . . . ① | S_{1}\cup S_{2} \dots \cup S_{m} | = \sum_{i} | S_{i} | - \sum_{i,j} | S_{i} \cap S_{j} |+ \sum_{i,j,k} | S_{i}\cap S_{j} \cap S_{k} |....+(-1)^{n-1}\sum | S_{1}\cap S_{2} \cap S_{3}... \cap S_{m} |........① S1S2Sm=iSii,jSiSj+i,j,kSiSjSk∣....+(1)n1S1S2S3...Sm∣........①
计算总共的项数:利用组合数计算,每次从n个元素里面选i个进行交集计算,故总项数
C n 1 + C n 2 + ⋯ + C n n = 2 n C_{n}^{1} + C_{n}^{2} + \dots + C_{n}^{n} = 2 ^ {n} Cn1+Cn2++Cnn=2n
时间复杂度为 O ( 2 n ) O(2^n) O(2n)
接下来验证的一下上面①式中每个元素是不是只算了一次(具体证明略):
假设 x ∈ S 1 ∪ S 2 ⋯ ∪ S n ,存在于 k 个集合之中, 1 ≤ k ≤ n 那么 x 被计算的次数为 C k 1 − C k 2 + C k 3 − C k 4 + ⋯ + ( − 1 ) k − 1 C k k = 1 假设x\in S_{1} \cup S_{2} \dots \cup S_{n},存在于k个集合之中,1\le k\le n\\那么x被计算的次数为C_{k}^{1} - C_{k}^{2}+C_{k}^{3}-C_{k}^{4}+ \dots + (-1)^{k-1}C_{k}^{k}=1 假设xS1S2Sn,存在于k个集合之中,1kn那么x被计算的次数为Ck1Ck2+Ck3Ck4++(1)k1Ckk=1
Acwing 890.能被整除的数
在这里插入图片描述实现思路:记Si为1~n中能被pi整除的集合,根据容斥原理,所有数的个数为各个集合的并集,计算公式:
∣ S 1 ∪ S 2 ⋯ ∪ S m ∣ = ∑ i ∣ S i ∣ − ∑ i , j ∣ S i ∩ S j ∣ + ∑ i , j , k ∣ S i ∩ S j ∩ S k ∣ . . . . + ( − 1 ) n − 1 ∑ ∣ S 1 ∩ S 2 ∩ S 3 . . . ∩ S m ∣ | S_{1}\cup S_{2} \dots \cup S_{m} | = \sum_{i} | S_{i} | - \sum_{i,j} | S_{i} \cap S_{j} |+ \sum_{i,j,k} | S_{i}\cap S_{j} \cap S_{k} |....+(-1)^{n-1}\sum | S_{1}\cap S_{2} \cap S_{3}... \cap S_{m} | S1S2Sm=iSii,jSiSj+i,j,kSiSjSk∣....+(1)n1S1S2S3...Sm

  • 每个集合Si就对应能被质数pi整除的数

  • 对于每个集合Si实际上并不需要知道含有哪些元素,只需要知道各个集合中元素个数,对于单个集合Si中元素个数就是对应质数pi的倍数个数(1~n范围内),计算公式为
    ∣ S i ∣ = n p i ,下取整 |S_i|=\frac{n}{p_i},下取整 Si=pin,下取整

  • 对于任意个集合交集中元素的个数:每个质数pi对应一个集合Si,那么
    ∣ S i ∩ S j ∣ = n p i ∗ p j ,下取整,即交集就是 p i 和 p j 的公倍数的个数 |S_i \cap S_j|=\frac{n}{p_i*p_j},下取整,即交集就是p_i和p_j的公倍数的个数 SiSj=pipjn,下取整,即交集就是pipj的公倍数的个数

  • 表示每个集合的状态(即选中几个集合的交集,)m个质数,需要m个二进制位表示,共 2 m − 1 2^m-1 2m1种情况(至少选中一个集合),个数前面的符号为(-1)^(n-1)。以m = 4为例,所以需要4个二进制位来表示每一个集合选中与不选的状态,若此时为1011,表示集合S1∩S3∩S4中元素的个数,同时集合个数为3,前面的符号为(-1)^(3-1)=1,即
    ∣ S 1 ∩ S 3 ∩ S 4 ∣ = ( − 1 ) 3 − 1 n p 1 ∗ p 3 ∗ p 4 |S_1 \cap S_3 \cap S_4|=(-1)^{3-1}\frac{n}{p_1*p_3*p_4} S1S3S4=(1)31p1p3p4n怎么取到一个数的每一个二进制位:使用位运算(第一章), 数i的第j位是否为1:i >> j & 1

注:用二进制表示状态的小技巧非常常用,后面的状态压缩DP也用到了这个技巧,因此一定要掌握

样例分析:
在这里插入图片描述
具体实现代码(详解版):

#include <iostream>

using namespace std;

typedef long long LL;
const int N = 20;
int p[N]; // 存储质数p[i]
int n, m; 

int main() {
    cin >> n >> m; 
    for (int i = 0; i < m; i++) cin >> p[i]; // 输入 m 个质数并存入数组 p 中
    
    int res = 0; // 结果初始化为 0
    // 枚举所有质数的子集状态,状态用二进制表示,从 1 到 (1 << m) - 1
    // 即从000...01到111...11,其中每一位表示是否选中对应的质数
    for (int i = 1; i < 1 << m; i++) {
        int t = 1, s = 0; // t 表示当前质数子集的乘积,s 表示选中的质数个数
        
        // 枚举每一个质数,判断它是否在当前子集中
        for (int j = 0; j < m; j++) {
            // 判断第 j 个质数是否被选中(通过位运算检查第 j 位是否为 1)
            if (i >> j & 1) {
                // 检查当前乘积是否超过 n,若超过则停止计算该子集
                if ((LL)t * p[j] > n) {
                    t = -1; // 标记当前子集不可行
                    break;
                }
                s++; // 记录当前子集中质数的个数
                t = (LL)t * p[j]; // 更新子集的质数乘积
            }
        }
        // 如果当前子集不可行(乘积超出范围),则跳过
        if (t == -1) continue;
        
        // 根据子集质数个数 s 的奇偶性更新结果:
        // 如果质数个数为奇数,则加上 n / t;如果为偶数,则减去 n / t
        if (s % 2) res += n / t; // 奇数个质数,容斥原理加上这个集合的贡献
        else res -= n / t; // 偶数个质数,容斥原理减去这个集合的贡献
    }
    
    cout << res << endl;
    
    return 0;
}


这道题利用了容斥原理,核心思想是通过枚举质数子集并计算其整除数量,来处理多个集合的并集大小问题。通过容斥加减,避免了重复计数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2184500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习西瓜书笔记(十三) 第十三章半监督学习+代码

第十三章 13 半监督学习13.1 未标记样本13.3.1 小结 13.2 生成式方法13.2.1 小结 13.3 半监督SVM13.3.1 小结 13.4 图半监督学习13.4.1 小结 13.5 基于分歧的方法13.5.1 小结 13.6 半监督聚类13.6.1 小结 13.7 代码&#xff1a;手写数据集上的标签传播-性能展示章末小结 13 半监…

netty之NettyServer群发消息

前言 在微信或者QQ的聊天中我们经常会用到一些群聊&#xff0c;把你的信息发送给所有用户。那么为了实现群发消息&#xff0c;在netty中我们可以使用ChannelGroup方式进行群发消息。如果为了扩展验证比如你实际聊天有不同的群&#xff0c;那么可以定义ConcurrentHashMap结构来存…

程计软考题2-编译、解释程序翻译阶段

(一) 编译器和解释器的工作阶段 1.编译和解释与源程序的区别 分析&#xff1a;编译和解释是语言处理的两种基本方式。 编译过程包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等阶段&#xff0c;以及符号表管理和出错处理模块。 解释过程在词法、语…

Nuxt.js 应用中的 app:rendered 钩子详解

title: Nuxt.js 应用中的 app:rendered 钩子详解 date: 2024/10/2 updated: 2024/10/2 author: cmdragon excerpt: 摘要:本文详细介绍了 Nuxt.js 应用程序中的 app:rendered 钩子,包括其定义、调用时机、上下文信息以及通过实际案例展示如何记录性能和发送日志到服务器。 …

基于大数据的大屏高速公路收费系统的开发设计与实现SpringBoot+vue

目录 1. 需求分析 2. 技术选型 3. 系统架构设计 4. 开发实现 5. 代码示例和效果演示 6. 持续优化 由于我国高速公路的建设和发展与国外先进国家有很大差距。在高速公路建成后&#xff0c;收费系统往往选用国外的成熟产品。虽然这些产品在功能上基本满足了高速公路收费的要…

如何用JavaScript编写一个简单的计数器

在网页开发中&#xff0c;计数器是一种常见的功能&#xff0c;它可以帮助我们记录点击次数、显示时间等。下面我将介绍如何在HTML页面中使用JavaScript实现一个基本的计数器。如图&#xff1a; 1、 创建HTML结构 首先&#xff0c;我们需要创建一个基础的HTML结构来容纳我们的计…

影视cms泛目录用什么程序?苹果cms二次开发泛目录插件

影视CMS泛目录一般使用的程序有很多种&#xff0c;&#xff08;maccmscn&#xff09;以下是其中几种常见的程序&#xff1a; WordPress&#xff1a;WordPress是一个非常流行的开源内容管理系统&#xff0c;可以通过安装一些插件来实现影视CMS泛目录功能。其中&#xff0c;一款常…

matlab初学习记录

文章目录 内置函数与变量matlab 编辑器数组等间距向量数组函数数组索引提取多个元素 对向量执行数组计算查看文档 画图添加注释 实践导入数据关系运算符分支恒星运动 matlab 学习看入门之旅 先计算等号右边再计算等号左边。 工作区记录等号右边的变量。 ; 表示的是抑制输出。…

vmvare虚拟机centos 忘记超级管理员密码怎么办?

vmvare虚拟机centos 忘记超级管理员密码怎么办?如何重置密码呢? 一、前置操作 重启vmvare虚拟机的过程中,长按住Shift键 选择第一个的时候,按下按键 e 进入编辑状态。 然后就会进入到类似这个界面中。 在下方界面 添加 init=/bin/sh,然后按下Ctrl+x进行保存退出。 init=/bi…

编码能力提升计划 - 华为OD统一考试(E卷)

2024华为OD机试(E卷+D卷+C卷)最新题库【超值优惠】Java/Python/C++合集 题目描述 为了提升软件编码能力,小王制定了刷题计划,他选了题库中的n道题,编号从0到n-1,并计划在m天内按照题目编号顺序刷完所有的题目(注意,小王不能用多天完成同一题)。 在小王刷题计划中,小王…

CSS样式基础样式选择器

目录 1.css样式的规则 2.引入css样式的方式 1)行内式 2)内嵌式 3)外链式 1-link导入 2-import导入 4)总 3.css基础选择器 1)标签选择器 案例&#xff1a;使用标签选择器编写一个圆 1.代码 2.效果 2)类选择器 案例&#xff1a;使用类选择器为div添加背景色 1.代码 2.效果 3)id…

如何使用ssm实现影院管理系统的设计与实现

TOC ssm751影院管理系统的设计与实现jsp 研究背景与现状 时代的进步使人们的生活实现了部分自动化&#xff0c;由最初的全手动办公已转向手动自动相结合的方式。比如各种办公系统、智能电子电器的出现&#xff0c;都为人们生活的享受提供帮助。采用新型的自动化方式可以减少…

多处理器的概念与对比

SISD, SIMD, MISD, 和 MIMD 代表了并行计算的四种基本架构&#xff0c;它们描述了处理器如何处理指令和数据。 理解这些架构的关键在于区分指令流&#xff08;Instruction Stream&#xff09;和数据流&#xff08;Data Stream&#xff09;是单一的还是多重的。 1. SISD (Singl…

怎样过好国庆节

今天是2024年10月1号&#xff0c;国庆节&#xff0c;七天小长假&#xff0c;估计每个人都有自己的小计划。有想出去浪的&#xff0c;有想闭关修炼的&#xff0c;有想约会恋爱的&#xff0c;也有想回家看父母的&#xff0c;只要有事干&#xff0c;有想法&#xff0c;有行动&…

【JavaEE】——多线程常用类

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 引入&#xff1a; 一&#xff1a;Callable和FutureTask类 1&#xff1a;对比Runnable 2&#xff1a…

多模态大模型 Qwen2-VL 下载、推理、微调实战案例来了

文章目录 技术交流Qwen2-VL 有什么新功能&#xff1f;模型结构模型效果模型下载模型推理模型微调 最近这一两周看到不少互联网公司都已经开始秋招发放Offer。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c…

c#增删改查 (数据操作的基础)

//数据操作无非4种 //增删改查 是数据操作的基础 int[] ints { 110, 120, 119 }; //1. 查 在这里就是获取数组中的数据 int num ints[1]; //将数组中的某个元素取出来 Console.WriteLine(num); //2. 改 将数据从…

【C++并发入门】opencv摄像头帧率计算和多线程相机读取(下):完整代码实现

前言 高帧率摄像头往往应用在很多opencv项目中&#xff0c;今天就来通过简单计算摄像头帧率&#xff0c;抛出一个单线程读取摄像头会遇到的问题&#xff0c;同时提出一种解决方案&#xff0c;使用多线程对摄像头进行读取。上一期&#xff1a;【C并发入门】摄像头帧率计算和多线…

Elasticsearch使用Easy-Es + RestHighLevelClient实现深度分页跳页

注意&#xff01;&#xff01;&#xff01;博主只在测试环境试了一下&#xff0c;没有发到生产环境跑。因为代码还没写完客户说不用弄了( •̩̩̩̩&#xff3f;•̩̩̩̩ ) 也好&#xff0c;少个功能少点BUG 使用from size的时候发现存在max_result_window10000的限制&…

认知杂谈67《耐心!征服世界的秘籍》

内容摘要&#xff1a; 人生需家人朋友支持&#xff0c;自信源于解决问题的实力。别怕挫折&#xff0c;努力向前&#xff0c;反思自我。人生如游戏&#xff0c;靠自己打拼。学习要提升沟通、逻辑思维和时间管理等技能&#xff0c;读经典书籍&#xff0c;在平台学编程等&#xff…