【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(包括 nums0 和 nums2):剩余元素 2 的按位或为 2。

2, 1(包括 nums1 和 nums2):剩余元素 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 <= numsi <= 10^6 1<=numsi<=106

SOSDP(高维前缀和DP、子集和DP)

dp0i = n u m s . c o u n t ( i ) nums.count(i) nums.count(i)

dp11=dp01+dp00 : 1的数量

dp13=dp03+dp02 3和2的数量

dp15=dp05+dp04 5和4的数量

dp17=dp07+dp07 6和7的数量

dp23=dp13+dp11 1,2,3的数量

dp27=dp17+dp15 4,5,6,7的数量

dp37=dp27+dp23 1到7的数量

dpbitmask记录符合以下三个条件的x的数量:

一,mask任意二进制位为0,则x的此二进制位位0。

二,mask的第i个二进制位为1,最低位是第0位。i<bit,x的第x位可以为0或1;否则必须为1。

cpp 复制代码
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];
						}
					}
				}

时间复杂度 :O(NlogN)

dpbit m \^ (1 \<\< bit) 和dpbit -1m \^ (1 \<\< bit) 相同。

dpbit+1m 只被读取修改一次,且是同一次修改。

故可以降维,忽略bit,只保留m。

cpp 复制代码
				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)];			
						}						
					}
				}

代码

核心代码

cpp 复制代码
template<long long MOD = 1000000007,class T1 = int, class T2 = long long>
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<class T>
	static int bitcount(T x) {
		int countx = 0;
		while (x) {
			countx++;
			x &= (x - 1);
		}
		return countx;
	}
	vector<int> m_vCnt;
};
		
        	typedef C1097Int<> BI;
		class Solution {
		public:
			int countEffective(vector<int>& 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<vector<int>> dp(M+1, vector<int>(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<long long MOD = 1000000007,class T1 = int, class T2 = long long>
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<class T>
	static int bitcount(T x) {
		int countx = 0;
		while (x) {
			countx++;
			x &= (x - 1);
		}
		return countx;
	}
	vector<int> m_vCnt;
};
		
       	typedef C1097Int<> BI;
		class Solution {
		public:
			int countEffective(vector<int>& 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<int> 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<int> nums;
		vector<vector<int>> 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);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法 用**C++**实现。

相关推荐
浪客灿心4 分钟前
项目篇:模块设计与实现
数据库·c++
牛油果子哥q16 分钟前
【C++ STL vector】C++ STL vector 终极精讲:动态数组底层原理、两倍扩容机制、迭代器失效、增删查改、性能剖析与工程避坑指南
开发语言·c++
happymaker06261 小时前
LeetCodeHot100——42.接雨水
算法
阿正的梦工坊2 小时前
【Rust】07-错误处理:Option、Result 与 ? 运算符
开发语言·算法·rust
为何创造硅基生物2 小时前
独占指针的创建std::make_unique 本身自带堆出现
c++
kyle~2 小时前
ROS 2 与 Isaac Sim 联合仿真(一)体系架构、环境选型与基础通信闭环
c++·机器人·nvidia·仿真·ros2
努力努力再努力wz3 小时前
【内存管理与高并发内存池系列】从 mmap 到 malloc:文件映射、匿名映射与 glibc 内存分配机制详解
linux·c语言·数据结构·数据库·c++·qt·链表
八解毒剂3 小时前
数据结构-平衡二叉树——对二叉搜索树的优化
数据结构·c++·算法
运行时记录3 小时前
别再手动写提示词了 — SkillOpt 让技能文档自己进化
算法
起床困难户5754 小时前
条款20:协助完成返回值优化
c++