题目链接
kickstart 18 roundB Sherlock and the Bit Strings
大意
寻找一个长度为 n n n 的字符串,满足以下条件:
- 满足 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}
- 并且是字典序第 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 p−1 的二进制表示。
不过,这种做法有一个缺点就是无法扩展到 large 的情况。
因此我们直接分析 large:
首先 B i − A i ≤ 15 B_i -A_i \le15 Bi−Ai≤15, 也就是说对于任意一个 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] p−dp[prf][i],即减去所有 i i i 位为0 的情况总数
code
总结小技巧
builtin_popcount
这个函数能很快的在位级计算有多少个1,显然要计算前
l
l
l位有多少个1可以如下计算
int cnt = popcnt(mask) - popcnt(((ui(-1)) <<l) & mask);