「闲话随笔」枚举子集与枚举子集的子集与枚举子集的子集的子集
「闲话随笔」套娃子集
为什么要写这么弱智的内容:我太菜了。
枚举子集作用
常用于状压 DP。
枚举一个状态的所有子集状态。
例如 1101
的子集有:
1101
1100
1001
1000
0101
0100
0001
0000
代码
for (ll i = u; i; i = (i - 1) & u) {
// i 是子集
}
原理
每次 i-1
后都会把当前状态的最后一个 1
后面的所有位变为 1
,这一位变为 0
。
再 &u
将不能为 1
的每一位变为零,保证当前枚举的是子集。
那么当前就可以枚举到 比上一个状态小的所有状态中最大的状态。
例如枚举 1101
的子集,当前到了 1100
。
进行第一步得 1011
,进行第二步得 1001
。
感觉讲了和没讲一样,感性理解吧。
复杂度
枚举一个状态的的复杂度显然是 \(2^n\) 的,这里主要讨论枚举 \(0\sim 2^n\) 的所有状态的所有子集的时间复杂度。
显然可得式子:
\[\sum_{i=0}^{n}\dbinom{n}{i}2^i
\]
发现很眼熟,用个谔项式定理套一下:
\[\begin{aligned}
\sum_{i=0}^{n}\dbinom{n}{i}2^i
&=\sum_{i=0}^{n}\dbinom{n}{i}2^i1^{n-i}\\
&=(2+1)^n\\
&=3^n
\end{aligned}
\]
得到了时间复杂度 \(\Theta(3^n)\)。
意义?
每一位有三种可能:
- 当前枚举的子集和状态这一位都是
0
; - 当前枚举的子集和状态这一位都是
1
; - 当前枚举的状态的这一位是
1
,但它的子集的这一位是0
。
乘法原理得 \(3^n\)。
套娃子集
其实刚才讨论的情况可以当成:枚举状态 \(2^n\) 的子集的子集。
那么我们考虑套娃:
\[\begin{matrix}
\underbrace{枚举状态 2^n的子集的子集\cdots的子集}\\
k个的子集
\end{matrix}
\]
时间复杂度:
\[\begin{aligned}
&\sum_{i_1=0}^{n}\dbinom{n}{i_1}\sum_{i_2=0}^{n}\dbinom{n}{i_2}\cdots\sum_{i_k=0}^{n}\dbinom{n}{i_k}1\\
=&\sum_{i_1=0}^{n}\dbinom{n}{i_1}\sum_{i_2=0}^{n}\dbinom{n}{i_2}\cdots\sum_{i_{k-1}=0}^{n}\dbinom{n}{i_{k-1}}2^n\\
=&\sum_{i_1=0}^{n}\dbinom{n}{i_1}(k-1)^n\\
=&k^n
\end{aligned}
\]
组合意义同上。
真是非常有趣的性质呢。
\[\Huge{\mathfrak{The\ End}}
\]