本文涉及知识点
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://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++\*\*实现。
