【SOSDP模板 容斥原理 逆向思考】3757. 有效子序列的数量|分数未知

本文涉及知识点

C++动态规划
容斥原理

LeetCode3757. 有效子序列的数量

数组的 强度 定义为数组中所有元素的 按位或 (Bitwise OR) 。

如果移除某个 子序列 会使剩余数组的 强度严格减少 ,那么该子序列被称为 有效子序列 。

返回数组中 有效子序列 的数量。由于答案可能很大,请返回结果对 109 + 7 取模后的值。

子序列 是一个 非空 数组,它是由另一个数组删除一些(或不删除任何)元素,并且不改变剩余元素的相对顺序得到的。

空数组的按位或为 0。

示例 1:

输入: nums = [1,2,3]

输出: 3

解释:

数组的按位或为 1 OR 2 OR 3 = 3。

有效子序列为:

1, 3\]:剩余元素 \[2\] 的按位或为 2。 \[2, 3\]:剩余元素 \[1\] 的按位或为 1。 \[1, 2, 3\]:剩余元素 \[\] 的按位或为 0。 因此,有效子序列的总数为 3。 示例 2: 输入: nums = \[7,4,6

输出: 4

解释:

数组的按位或为 7 OR 4 OR 6 = 7。

有效子序列为:

7\]:剩余元素 \[4, 6\] 的按位或为 6。 \[7, 4\]:剩余元素 \[6\] 的按位或为 6。 \[7, 6\]:剩余元素 \[4\] 的按位或为 4。 \[7, 4, 6\]:剩余元素 \[\] 的按位或为 0。 因此,有效子序列的总数为 4。 示例 3: 输入: nums = \[8,8

输出: 1

解释:

数组的按位或为 8 OR 8 = 8。

只有子序列 [8, 8] 是有效的,因为移除它会使剩余数组为空,按位或为 0。

因此,有效子序列的总数为 1。

示例 4:

输入: nums = [2,2,1]

输出: 5

解释:

数组的按位或为 2 OR 2 OR 1 = 3。

有效子序列为:

1\]:剩余元素 \[2, 2\] 的按位或为 2。 \[2, 1\](包括 nums\[0\] 和 nums\[2\]):剩余元素 \[2\] 的按位或为 2。 \[2, 1\](包括 nums\[1\] 和 nums\[2\]):剩余元素 \[2\] 的按位或为 2。 \[2, 2\]:剩余元素 \[1\] 的按位或为 1。 \[2, 2, 1\]:剩余元素 \[\] 的按位或为 0。 因此,有效子序列的总数为 5。 提示: 1 \< = n u m s . l e n g t h \< = 1 0 5 1 \<= nums.length \<= 10\^5 1\<=nums.length\<=105 1 \< = n u m s \[ i \] \< = 1 0 6 1 \<= nums\[i\] \<= 10\^6 1\<=nums\[i\]\<=106 ## SOSDP(高维前缀和DP、子集和DP) dp\[0\]\[i\] = n u m s . c o u n t ( i ) nums.count(i) nums.count(i) dp\[1\]\[1\]=dp\[0\]\[1\]+dp\[0\]\[0\] : 1的数量 dp\[1\]\[3\]=dp\[0\]\[3\]+dp\[0\]\[2\] 3和2的数量 dp\[1\]\[5\]=dp\[0\]\[5\]+dp\[0\]\[4\] 5和4的数量 dp\[1\]\[7\]=dp\[0\]\[7\]+dp\[0\]\[7\] 6和7的数量 dp\[2\]\[3\]=dp\[1\]\[3\]+dp\[1\]\[1\] 1,2,3的数量 dp\[2\]\[7\]=dp\[1\]\[7\]+dp\[1\]\[5\] 4,5,6,7的数量 dp\[3\]\[7\]=dp\[2\]\[7\]+dp\[2\]\[3\] 1到7的数量 dp\[bit\]\[mask\]记录符合以下三个条件的x的数量: 一,mask任意二进制位为0,则x的此二进制位位0。 二,mask的第i个二进制位为1,最低位是第0位。i\ class C1097Int { public: C1097Int(T1 iData = 0) :m_iData(iData% MOD) { } C1097Int(T2 llData) :m_iData(llData% MOD) { } C1097Int operator+(const C1097Int& o)const { return C1097Int(((T2)m_iData + o.m_iData) % MOD); } C1097Int& operator+=(const C1097Int& o) { m_iData = ((T2)m_iData + o.m_iData) % MOD; return *this; } C1097Int& operator-=(const C1097Int& o) { m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD; return *this; } C1097Int operator-(const C1097Int& o)const { return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD); } C1097Int operator*(const C1097Int& o)const { return((T2)m_iData * o.m_iData) % MOD; } C1097Int& operator*=(const C1097Int& o) { m_iData = ((T2)m_iData * o.m_iData) % MOD; return *this; } C1097Int operator/(const C1097Int& o)const { return *this * o.PowNegative1(); } C1097Int& operator/=(const C1097Int& o) { *this *= o.PowNegative1(); return *this; } bool operator==(const C1097Int& o)const { return m_iData == o.m_iData; } bool operator<(const C1097Int& o)const { return m_iData < o.m_iData; } C1097Int pow(T2 n)const { C1097Int iRet = (T1)1, iCur = *this; while (n) { if (n & 1) { iRet *= iCur; } iCur *= iCur; n >>= 1; } return iRet; } C1097Int PowNegative1()const { return pow(MOD - 2); } T1 ToInt()const { return ((T2)m_iData + MOD) % MOD; } private: T1 m_iData = 0;; }; class CBitCounts { public: CBitCounts(int iMaskCount) { m_vCnt.resize(iMaskCount); for (int i = 1; i < iMaskCount; i++) { m_vCnt[i] = 1 + m_vCnt[i & (i - 1)]; } } template static int bitcount(T x) { int countx = 0; while (x) { countx++; x &= (x - 1); } return countx; } vector m_vCnt; }; typedef C1097Int<> BI; class Solution { public: int countEffective(vector& nums) { const int N = nums.size(); int iMax = 0; for (const auto& n : nums) { iMax |= n; } int M = 0, MC; for (; (1 << M)<= iMax; M++); MC = 1 << M; vector> dp(M+1, vector(MC)); for (const auto& n : nums) { dp[0][n]++; } for (int bit = 0; bit < M; bit++) { for (int m = 0; m < MC; m++) { if (m & (1 << bit)) { dp[bit+1][m] = dp[bit ][m] + dp[bit ][m ^ (1 << bit)]; } else { dp[bit + 1][m] = dp[bit][m]; } } } CBitCounts bc(MC); C1097Int<> ans; for (int m = 0; m <= iMax; m++) { if ((m | iMax) != iMax) { continue; } BI cur = BI(2).pow(dp.back()[m]) - 1; if(1& (bc.m_vCnt[m] + bc.m_vCnt[iMax])){ ans -= cur; } else { ans += cur; } } return (BI(2).pow(N)-ans).ToInt(); } }; ``` ### 第二版代码 ```cpp template class C1097Int { public: C1097Int(T1 iData = 0) :m_iData(iData% MOD) { } C1097Int(T2 llData) :m_iData(llData% MOD) { } C1097Int operator+(const C1097Int& o)const { return C1097Int(((T2)m_iData + o.m_iData) % MOD); } C1097Int& operator+=(const C1097Int& o) { m_iData = ((T2)m_iData + o.m_iData) % MOD; return *this; } C1097Int& operator-=(const C1097Int& o) { m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD; return *this; } C1097Int operator-(const C1097Int& o)const { return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD); } C1097Int operator*(const C1097Int& o)const { return((T2)m_iData * o.m_iData) % MOD; } C1097Int& operator*=(const C1097Int& o) { m_iData = ((T2)m_iData * o.m_iData) % MOD; return *this; } C1097Int operator/(const C1097Int& o)const { return *this * o.PowNegative1(); } C1097Int& operator/=(const C1097Int& o) { *this *= o.PowNegative1(); return *this; } bool operator==(const C1097Int& o)const { return m_iData == o.m_iData; } bool operator<(const C1097Int& o)const { return m_iData < o.m_iData; } C1097Int pow(T2 n)const { C1097Int iRet = (T1)1, iCur = *this; while (n) { if (n & 1) { iRet *= iCur; } iCur *= iCur; n >>= 1; } return iRet; } C1097Int PowNegative1()const { return pow(MOD - 2); } T1 ToInt()const { return ((T2)m_iData + MOD) % MOD; } private: T1 m_iData = 0;; }; class CBitCounts { public: CBitCounts(int iMaskCount) { m_vCnt.resize(iMaskCount); for (int i = 1; i < iMaskCount; i++) { m_vCnt[i] = 1 + m_vCnt[i & (i - 1)]; } } template static int bitcount(T x) { int countx = 0; while (x) { countx++; x &= (x - 1); } return countx; } vector m_vCnt; }; typedef C1097Int<> BI; class Solution { public: int countEffective(vector& nums) { const int N = nums.size(); int iMax = 0; for (const auto& n : nums) { iMax |= n; } int M = 0, MC; for (; (1 << M)<= iMax; M++); MC = 1 << M; vector dp(MC); for (const auto& n : nums) { dp[n]++; } for (int bit = 0; bit < M; bit++) { for (int m = 0; m < MC; m++) { if (m & (1 << bit)) { dp[m] = dp[m] + dp[m ^ (1 << bit)]; } } } CBitCounts bc(MC); C1097Int<> ans; for (int m = 0; m <= iMax; m++) { if ((m | iMax) != iMax) { continue; } BI cur = BI(2).pow(dp[m]) - 1; if(1& (bc.m_vCnt[m] + bc.m_vCnt[iMax])){ ans -= cur; } else { ans += cur; } } return (BI(2).pow(N)-ans).ToInt(); } }; ``` ### 单元测试 ```cpp vector nums; vector> queries; TEST_METHOD(TestMethod001) { nums = { 1}; auto res = Solution().countEffective(nums); AssertEx(1, res); } TEST_METHOD(TestMethod11) { nums = { 1,2,3 }; auto res = Solution().countEffective(nums); AssertEx(3, res); } TEST_METHOD(TestMethod12) { nums = { 7,4,6 }; auto res = Solution().countEffective(nums); AssertEx(4, res); } TEST_METHOD(TestMethod13) { nums = { 8,8 }; auto res = Solution().countEffective(nums); AssertEx(1, res); } TEST_METHOD(TestMethod14) { nums = { 2,2,1 }; auto res = Solution().countEffective(nums); AssertEx(5, res); } TEST_METHOD(TestMethod15) { nums = { 8,12 }; auto res = Solution().countEffective(nums); AssertEx(2, res); } ``` ![](https://img-blog.csdnimg.cn/8d37dcd13ddb4df9af8f95fefd86828d.gif) ## 扩展阅读 | 我想对大家说的话 | |----------------------------------------------------------------------------------------------------------------------------------------------------------------| | 工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《[算法与数据汇总](https://blog.csdn.net/he_zhidan/article/details/137131684)》。 | | 学习算法:按章节学习《[喜缺全书算法册](https://download.csdn.net/download/he_zhidan/88348653)》,大量的题目和测试用例,[打包下载](https://blog.csdn.net/he_zhidan/article/details/140733677)。重视操作 | | 有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 | | 员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。 | | 闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 | | 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 | | 如果程序是一条龙,那算法就是他的是睛 | | 失败+反思=成功 成功+反思=成功 | ### 视频课程 先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。 如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程 ### 测试环境 操作系统:win7 开发环境: VS2019 **C++17** 或者 操作系统:win10 开发环境: VS2022 **C++17** 如无特殊说明,本**算法** 用\*\*C++\*\*实现。 ![](https://i-blog.csdnimg.cn/blog_migrate/4b48f80cdf99b7ea9bda88ceb91d788f.gif)

相关推荐
Xy-unu2 小时前
[LLM]AIM: Adaptive Inference of Multi-Modal LLMs via Token Merging and Pruning
论文阅读·人工智能·算法·机器学习·transformer·论文笔记·剪枝
Hcoco_me2 小时前
算法选型 + 调参避坑指南
算法
Jul1en_2 小时前
【算法】分治-归并类题目
java·算法·leetcode·排序算法
kangk122 小时前
统计学基础之概率(生物信息方向)
人工智能·算法·机器学习
再__努力1点2 小时前
【77】积分图像:快速计算矩形区域和核心逻辑
开发语言·图像处理·人工智能·python·算法·计算机视觉
唯唯qwe-3 小时前
Day22: 贪心算法 | 区间问题,左/右端点排序
算法·贪心算法
Hcoco_me3 小时前
LLM(Large Language Model)系统学习路线清单
人工智能·算法·自然语言处理·数据挖掘·聚类
java修仙传3 小时前
力扣hot100:寻找旋转排序数组中的最小值
算法·leetcode·职场和发展
胖咕噜的稞达鸭3 小时前
算法日记专题:位运算II( 只出现一次的数字I II III 面试题:消失的两个数字 比特位计数)
c++·算法·动态规划
茉莉玫瑰花茶3 小时前
ProtoBuf - 3
服务器·c++·protobuf