• 浅析容斥和DP综合运用


    浅析容斥和DP综合运用

    前言

    众所周知在数数题中有一种很重要的计数方法——容斥。但是容斥有一个很大的缺陷:枚举子集的复杂度过高。所以对于数据规模较大的情况会很乏力,那么我们就只能引入容斥DP。

    复习一下容斥

    什么情况下用容斥?容斥能干什么?

    容斥的基本功能就是当你知道任意个指定集合的交集,你就能推出这些集合的并集。

    形式化的来说,就是:

    [left|igcup_{i=1}^{n} A_{i} ight|=sum_{i=1}^{n}left|A_{i} ight|-sum_{1 leq i<j leq n}left|A_{i} cap A_{j} ight|+sum_{1 leq i<j<k leq n}left|A_{i} cap A_{j} cap A_{k} ight|-cdots+(-1)^{n-1}left|A_{1} cap cdots cap A_{n} ight| ]

    只使用容斥朴素算法

    如果我们只会容斥,我们该怎么做?很显然根据上面的公式,我们需要枚举任意集合的组合方式,然后统计他们的答案,将他们加入答案。

    比如说在【线上训练 5】乘方中,当我们枚举出子集,我们就很容易求出子集的大小。

    【线上训练3】数个数,当我们枚举出了子集,我们也能统计出子集的大小

    我们通过以上两道题,总结出了这种容斥题的一个特点:都是求集合的并集,同时你可以通过一些方式求得集合的交集。

    使用DP进行优化

    我们思考一下就会发现,上面两道题的复杂度瓶颈都在于需要(2^k)的枚举出所有的子集再进行DP。那我们就可以考虑进行DP。因为对于一个子集,添加一个元素,就会导致他贡献的符号取反。

    一般DP状态都是(dp[i][j]),其中(i)代表前(i)个集合中的元素。而(j)一般代表一个决定交集大小的值。而对于(j)值相同的所有状态(子集),在它们之后再添加一个元素,对答案增加的贡献都一样。

    举个例子:

    【线上训练3】数个数中,如果往一个子集内加入新的元素,子集的大小就会增加((字符集)^{(加入的区间位置-上一个区间位置)})。所以我们记录的(j)就是上一个区间的位置。
    而在【线上训练 5】乘方中,如果往一个子集内加入新的元素,子集的大小就会变成(lcm(j,N_i))。所以(j)记录的就是选择的子集的(lcm)

    对于前一道题而言,因为决定所选子集的大小是子集中元素的间隔距离。所以我们需要一边(dp)选择元素,一边把每一次往子集里添加元素增加的贡献累加进入最终答案。

    而对于后一道题而言,因为决定所选子集的大小是子集中元素的(lcm),因为这是一个数,而且这个数和前面说的转移答案所需要的(j)是同一个数,所以我们可以只在(dp)数组里记录容斥系数的和,等到最后再来统计答案。

  • 相关阅读:
    深入理解 ProtoBuf 原理与工程实践(概述)
    高性能缓存 Caffeine 原理及实战
    Java 多线程上下文传递在复杂场景下的实践
    SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
    MySQL 5.6.35 索引优化导致的死锁案例解析
    gitlab安装升级(大版本跨度9.4.5----13.2.1)
    mysql 查看表的索引
    python安装mysql库 ,MySQL-python
    Alpine包管理工具apk使用介绍
    docker容器添加hosts
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/11726437.html
Copyright © 2020-2023  润新知