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

文章目录

  • 题目链接
    • 大意
  • 分析
    • 状态转移
    • 构造解
  • code
  • 总结小技巧

题目链接

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);

你可能感兴趣的:(算法刷题,google,kickstart,18/19)