TonyYin's Blog

Back

位运算技巧#

位运算功能示例
x>>k\texttt{x>>k}去掉最后 kk101101101,k=3101101\rightarrow 101, k=3
x<<k\texttt{x<<k}在最后加 kk00101101101101000,k=3101101\rightarrow 101101000, k=3
$\texttt{x<<11}$在最后加一个 11
$\texttt{x1}$把第 11 位变成 11
$\texttt{x(1<<(k-1))}$把第 kk 位变成 11
$\texttt{x((1<<k)-1)}$把末 kk 位变成 11
x&(-2)\texttt{x\&(-2)}把第 11 位变成 00101011110101101010111\rightarrow 1010110
x & ( (1<<(k-1)))\texttt{x \& (~(1<<(k-1)))}把第 kk 位变成 0010101101000110,k=51010110\rightarrow 1000110, k=5
x&(-(1<<k))\texttt{x\&(-(1<<k))}把末 kk 位变成 0010101101000000,k=51010110\rightarrow 1000000, k=5
x ˆ 1\texttt{x \^{} 1} 把第 11 位取反101101101100101101\rightarrow 101100
x ˆ (1<<(k-1))\texttt{x \^{} (1<<(k-1))}把第 kk 位取反101001101101,k=3101001\rightarrow 101101, k=3
x ˆ ((1<<k)-1)\texttt{x \^{} ((1<<k)-1)}把末 kk 位取反11011011100010,k=41101101\rightarrow 1100010, k=4
x&7\texttt{x\&7}取末 3311011011011101101\rightarrow 101
x&(1<<(k-1))\texttt{x\&(1<<(k-1))}取末 kk110110101101,k=31101101\rightarrow 01101, k=3
(x>>(k-1))&1\texttt{(x>>(k-1))\&1}取第 kk11011011,k=41101101\rightarrow 1, k=4
x&(x+1)\texttt{x\&(x+1)}把末尾连续的 11 变成 00100101111100100000100101111\rightarrow 100100000
$\texttt{x(x+1)}$把右边第一个 00 变成 11
$\texttt{x(x-1)}$把末尾连续的 00 变成 11
(x ˆ (x+1))>>1\texttt{(x \^{} (x+1))>>1}取末尾连续的 111001011111111100101111\rightarrow 1111
x&(-x)\texttt{x\&(-x)}取右边第一个 11 及其右侧1001010001000100101000\rightarrow 1000

枚举子集、子集的子集#

枚举子集#

TT 表示 SS 的子集,则一种枚举子集的方式是:

1for (int T = S; T; T=(T - 1) & S)2Do Something (T is a nonempty subset of S)\begin{array}{ll} 1& \textbf{for }( \textbf{int } \text{T = S; T; T=(T - 1) \& S)}\\ 2& \qquad \text{Do Something (T is a nonempty subset of S)} \end{array}

单次枚举 TT 的复杂度是 O(2S)\mathcal{O}(2^{|S|}) 的。

枚举子集的子集#

若要枚举 SS 的所有子集的子集,可以证明复杂度为 O(3n)\mathcal{O}(3^n).

组合意义天地灭,代数推导保平安。

复杂度为:

TS2T=i=1n(ni)2i=i=1n(ni)2i1ni=(1+2)n1O(3n)\begin{aligned} \sum_{T\subseteq S}2^{|T|}=\sum_{i=1}^n\binom{n}{i} 2^i&=\sum_{i=1}^n\binom{n}{i}2^i1^{n-i}\\ &=(1+2)^n-1\Rightarrow \mathcal{O}(3^n) \end{aligned}

证毕,参考 枚举子集为什么是 O(3^n) 的 - Jijidawang - 博客园

题意#

参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nn 个深埋在地下的宝藏屋, 也给出了这 nn 个宝藏屋之间可供开发的 mm 条道路和它们的长度。

小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路则相对容易很多。

小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏屋之间的道路无需再开发。

新开发一条道路的代价是 L×K\mathrm{L} \times \mathrm{K}。其中 LL 代表这条道路的长度,KK 代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。

请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值。

对于 100% 100\% 的数据: 1n121 \le n \le 120m1030 \le m \le 10^3v5×105v \le 5\times 10^5

题解#

状压DP。

找个生成树,代价和最小。

fS,if_{S, i} 表示当前包含的点集是 SS,树高是 ii,此时的最小代价和。

则:

fS,i=minTS{fT,i1+w(T,S)}f_{S, i}=\min_{T\subseteq S} \{f_{T, i-1}+w(T, S)\}

其中 w(T,S)w(T, S) 代表从 TT 变到 SS 的代价,预处理。

预处理时,先找新增加的点,再找从 TT 连到这个点的最小边权,求和即可。顺便判断不合法的 (T,S)(T, S) 组合。

【洛谷-P3959】[NOIP2017 提高组] 宝藏
https://www.tonyyin.top/blog/oi-solution/p3959
Author TonyYin
Published at October 28, 2022
Comment seems to stuck. Try to refresh?✨