状态压缩动态规划

3n枚举子集

状压DP中相当重要的技巧(虽然后位有FWT,FMT替代,但不是都能代)

for(int i = x; i; i = (i - 1) & x) {
// i 就是 x 的子集
}

题目

P6622 [省选联考 2020 A/B 卷] 信号传递

看数据范围,m23,且不同分数段增长很慢,表明会有O(2m)的做法,考虑状压或搜索剪枝

但是题目要求的是排列的贡献,直接状压不可行,需要转化为与顺序无关的情况

观察到n105,表明对S序列要有很快的统计方式

关键转换:发现s[i1]s[i]之间的转移只会有m2种,预处理出s[]中每种的转移次数,那么此时数字i和数字j(设重排列后ij前面)之间的贡献为:

Wi,j=cnt[i][j](pos[j]pos[i])+kcnt[j][i](pos[j]+pos[i])

=pos[j](cnt[i][j]+kcnt[j][i])+pos[i](kcnt[j][i]cnt[i][j])

此时i,j的贡献分为两部分,分别仅与pos[i],pos[j]相关,可以分别统计;pos[i],pos[j]可以在枚举位置时分别获取,而剩下部分仅与i,j的相对位置有关,状压可以做到

方程很好推,时间复杂度O(m22m),过不了后40分

考虑i插入时对S的影响是一定的,可以在dp外预处理出g[i][S](想想如何做到O(m2m)),空间又炸了

接下来就是空间优化了 link

反思

被djy薄纱了,难受

考试时思路走错了,以为是直接替换s[],然后关键的预处理没想到,之后应该就是自然的思路了

关键在于看到数据范围就朝着状压的方向走,不断尝试消除排列的影响

P3959 [NOIP2017 提高组] 宝藏

思路来源

读题,一眼看出状态,但发现缺少层数,无法转移

考虑强制限定层数,所有新加点强制对上一层连边,而不是任意层

感性理解该状态转移完全

p.s. 开始考虑强制选定前一个点,发现这样子树状态还要上传,有后效性,没想到是按层转移(悲

P7519 [省选联考 2021 A/B 卷] 滚榜

最朴素方程非常好列,把状态、总和,前一个数,前一个数取的值全部放入方程即可,但是没有穷举+贪心快

重要性质:由于b[i]单调不降,一个常见(?)的思路就是考虑横向统计,如图

这说明我们不需要考虑前一个b[]取了多少,因为当前的b[]就是基于前面的b[],相当于把往后的分值同时向上顶了b[i1],此时想要方案合法,只需考虑a[i]a[i1]之间差了多少即可,类似差分思想

p.s. 卡常技巧:把大循环的int变为宏会更快

根号分治

做题时,如果遇到与质因数有关的状压DP,考虑根号分治

原理:我们约定大于n的数为大质数,则x[2,n],都有x的质因数中,要么没有大质数,要么有且仅有一个

这样我们就在x唯一对应了一个大质数,利用该性质,可以解决此类问题

P2150 [NOI2015] 寿司晚宴

朴素方程略

很明显,对于一个大质数x,要么小G取,要么小W取,要么都不取,那么把所有对应此大质数x的数放在一起,分g1[][]g2[][]分别统计x被小G取、小W取的方案数,再合并至f[][]中即可

p.s. 注意g1g2会有重复计数(都不取),记得减掉

P8292 [省选联考 2022] 卡牌

这道题有两种做法:FWT和容斥

题解都说样例提示了正难则反,但是我觉得FWT更自然……

做法1:暴力状压+FWT优化枚举子集

先根号分治,设f[i][S]为大质数为i,小质数状态为S

对大质数不同的数分开跑f[i][]

那么最后合并答案f0[S]i|j=Sf0[i]fk[j],可以用FWT优化

做法2:容斥

看看题目要求的是什么,不要容斥错了

其实第一眼发现si30的状压是可以直接转移的,问题在于统计是枚举,考虑在这里优化

题目求的是质数集至少S,子集反演直接pass

考虑容斥中常见的钦定大法,(不能钦定x一定出现,因为这样得到的就是题目求的质数集至少为S的方案数,绕一圈又回来了)

我们考虑钦定g[S]S一定不出现的方案数,则f(S)=TS(1)|T|g(T)

此时g(T)的贡献变得更简单:对于质数集为P的数x,若PT=,则x可被g(T)统计到,则g(T)=2cnt[T]

考虑大质数时也一样,只不过对于要求选取的大质数必须选一个,即g(T)=2cnt[T]1,最后根据乘法原理把每个大质数的g乘起来即可

技巧:如果每次都把大质数的g全部累乘,那时间复杂度就会有O(m|S|214)S2000以内的质数集),无法承受;考虑只有给定的大质数才有必须选的需求,对于其它大质数,直接g(T)=2cnt[T]=2cnt[T]计算即可,复杂度O(214c)

快速沃尔什变换(FWT)

求解Ci=jk=iAjBk,其中or,and,xor

FWT类似于FFT,是基于反演优化的(FWT更直观一点),实际上就是找一个式子FWT[Ai]=Aj,使得FWT[Ci]恰好等于FWT[Ai]FWT[Bi],从而用类似多项式点值表示的方法优化卷积

或卷积

这里找到的式子为FWT[Ai]=j|i=iAj,下面是证明

FWT[Ai]FWT[Bi]=(j|i=iAj)(k|i=iBk)=(j|k)|i=iAjBk=FWT[Ci]

接下来就是高效处理出FWT[A]了,高维前缀和可以做到,不过考虑类似FFT的分治做法,在合并[l,mid],[mid+1,r]时,有公式FWT[lr]=merge(FWT[lmid],FWT[lmid]+FWT[mid+1r])

转成二进制形式,A0为当前最高位为0A1为当前最高位为1,有

FWT[A]=merge(FWT[A0],FWT[A0]+FWT[A1])

还原数组时减回来即可

IFWT[A]=merge(IFWT[A0],IFWT[A0]IFWT[A1])

与卷积

FWT[A]=merge(FWT[A0]+FWT[A1],FWT[A1])

IFWT[A]=merge(IFWT[A0]IFWT[A1],IFWT[A1])

异或卷积

FWT[A]=merge(FWT[A0]+FWT[A1],FWT[A0]FWT[A1])

IFWT[A]=merge(IFWT[A0]+IFWT[A1]2,IFWT[A0]IFWT[A1]2)

子集卷积

即求h(S)=i|j=S,i&j=f(i)g(j)

子集反演

当方程f[S]难以快速统计转移时,考虑弱化它,然后容斥

具体地,设f[S]为状态集恰好S时的方案数,g[S]为状态集至多S时的方案数

即只能从S中取数,但不要求全部取完,此时g[S]的优势在于不用枚举子集,直接传S即可

此时有容斥:f(S)=TS(1)|S||T|g(T)

证明略

P3349 [ZJOI2016]小星星

首先得想到枚举子集,向儿子递归转移,时间复杂度O(n33n)

然后考虑把恰好为S换成至多为S,做子集反演,每次转移直接将S下传,将答案容斥即可

注意这里有个技巧:不需要使得编号对应不重(即树上两个点可能对应图上的同一个点),因为对于答案f[111...],需要n个点恰好取满,这种情况必不会重

p.s. 常数优化:提取每次S的点集,还有不把i分配的编号q[i]放入递归参数(亲测快5倍,可能是开栈相对循环太慢了)

C. 拼凑数字

给你n个数字,你需要将它们拼成一个n位整数 ,使得在保证nk取模的结果最大的前提下, 尽可能大。n24,k80

首先得想到朴素状压(逆天,我考时没想到

f[S][i]为取数状态为S,余数为i的最大数,转移即可

然后想到每个数只能是19,记录每种数字用了几次即可

通过计算可知,状态数最多为1.1×105,可以接受

但是对于非01串的状压,可能需要一点优化

技巧

P3813 [FJOI2017]矩阵填数

常见思路:最大值为v方案数=最大值v的方案数最大值<v的方案数

但是在这里有多个矩形,直接做会有问题,因为非法方案应该是存在一个矩形最大值<v,看n的范围想到容斥

上公式:f(S)=TS(1)|S||T|g(T)

钦定T为最大值<v的矩形集合,问题转化为快速求出g(T)

考虑离散化之后,(xi,yi),(xi+1,yi+1)一定可以表示最大值限制相同的矩形,枚举离散化后的小矩形统计即可

时间复杂度O(n32n)

p.s. 这么好的容斥题被我浪费了,我真该死啊——

轮廓线

用01序列描述二维矩阵上的凸多边形(即坐标单调)

P4363 [九省联考 2018] 一双木棋 chess

轮廓线DP

注意轮廓线还原为状态的技巧及逆序转移

由于这里求两人价值差,转移时把自己强行当作先手,逆推时对f[nxt]取负即可

posted @   Zhone_lb  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示