kickstart 18 roundB Sherlock and the Bit Strings(状压dp/计数)

博客探讨了如何解决kickstart 18 roundB中的Sherlock and the Bit Strings问题。通过固定Ai并利用二进制表示找到长度为n的字符串,满足特定条件且字典序为p。针对大型问题,通过枚举前16位字符,计算以特定前缀结束的字符串种类数。状态转移方程为dp[prf][i]=∑dp[left_shift(prf)and append0][i+1]+dp[left_shift(prf)and append1][i+1]。根据dp数组构造解,确定每个位置的0或1取值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

kickstart 18 roundB Sherlock and the Bit Strings

大意

寻找一个长度为 n n n 的字符串,满足以下条件:

  1. 满足 k k k 个条件: ∑ j = A i B i s j = C i , i ∈ { 1 , 2 , … , k } \sum_{j=A_i}^{B_i}s_j=C_i,i\in \{1,2,\dots,k\} j=AiBisj=Ci,i{1,2,,k}
  2. 并且是字典序第 p p p

分析

因为small所满足的限制是 A i = B i A_i=B_i Ai=Bi,
所以,
small 有一个非常简单的做法:

将所有的 A i A_i Ai 固定住,那么剩余位置第 p p p 大 就相当于是求 p − 1 p-1 p1 的二进制表示。

不过,这种做法有一个缺点就是无法扩展到 large 的情况。

因此我们直接分析 large:

首先 B i − A i ≤ 15 B_i -A_i \le15 BiAi15, 也就是说对于任意一个 position i i i, 我们如果知道他的前 16 16 16 位字符,那么我们一定能判断到目前为止位置 i i i 是否 valid 因此一个简单的做法就是我们对于 位置 i i i 枚举所有的 2 16 2^{16} 216 种情况,计算以这种前缀在 i i i 结尾的种类的个数(显然,前缀相同种类数一定是相同的),即计算 d p [ p r f ] [ i ] dp[prf][i] dp[prf][i],在 i位,包含 i i i,前缀状态是 p r f prf prf 的种类个数

状态转移

接下来我么你来看状态转移:

d p [ p r f ] [ i ] = ∑ d p [ l e f t _ s h i f t ( p r f ) a n d   a p p e n d 0 ] [ i + 1 ] + d p [ l e f t _ s h i f t ( p r f ) a n d   a p p e n d 1 ] [ i + 1 ] dp[prf][i] = \sum dp[left\_shift(prf)and\ append 0][i+1]+dp[left\_shift(prf)and\ append 1][i+1] dp[prf][i]=dp[left_shift(prf)and append0][i+1]+dp[left_shift(prf)and append1][i+1]

对于每一个位置 i i i 我们 check 所有 B j = i B_j=i Bj=i 的条件,如果有一个不满足,那么 d p [ p r f ] [ i ] = 0 dp[prf][i]=0 dp[prf][i]=0

构造解

构造解的过程很简单,如果 dp[prf][i],如果 prf&1==0,即第 i位为 0 0 0 的状态 ≥ p \ge p p 种,那么显然这一位取 0 0 0, 否则取 1 1 1,

如果第 i i i 位取的是 1 1 1, 那么我们后续计算的时候应该用 p − d p [ p r f ] [ i ] p-dp[prf][i] pdp[prf][i],即减去所有 i i i 位为0 的情况总数

code

github

总结小技巧

builtin_popcount这个函数能很快的在位级计算有多少个1,显然要计算前 l l l位有多少个1可以如下计算

int cnt = popcnt(mask) - popcnt(((ui(-1)) <<l) & mask);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值