题解:SP1182 SORTBIT - Sorted bit squence

题意

将区间 [m,n] 的所有整数按照其二进制位表示的数中 1 的数量从小到大排序。如果 1 的数量相同,则按照数的大小排序。求序列中第 k 个数。

其中,负数使用补码来表示:一个负数的二进制数与其相反数的二进制数之和恰好等于 232

分析

考虑用 uint32_t 存储负数,这样得到的就是二进制数就是符合题目描述的补码。

根据条件 m×n0 得到 m,n 是符号相同的,那么将 [m,n] 区间内的数转换成 uint32_t 后在值域上是连续的。(除了 0,所以需要对 0 进行特判)


先讨论第一个排序条件:按照其二进制位表示的数中 1 的数量从小到大排序。

考虑实现这样一种操作:查询区间 [l,r] 内二进制下 1 的个数为 c 的数的个数。

显然在左端点固定的情况下随着右端点增加操作的结果是单调不降的。

132 依次枚举 c,得到排序后第 k 个数二进制下 1 的个数 c 以及排序后第 k 个数在区间 [l,r] 内二进制下 1 的个数为 c 的数中的排名 k

这一部分说的有点抽象,建议结合代码理解。


得到了第 k 个数二进制下 1 的个数以及排名 k 后,根据第二个排序条件,考虑在 [m,n] 中二分。

查找第一个 i 使得区间 [l,i] 内二进制下 1 的个数为 c 的数等于 k

int 下输出这个数即可。


现在我们只需要实现这个操作。

发现这个操作比较典型,使用数位 dp 解决。

时间复杂度 O(Tlog3n)

code

#include<bits/stdc++.h>
using namespace std;
int dp[40][40][2];
typedef bitset<32> uint_t;
uint32_t dfs(int pos, int cnt, int num, const uint_t &upr, bool up, int tcnt)
{
if(pos==-1) return cnt==tcnt;
if(cnt>tcnt) return 0;
if(!up&&dp[pos][tcnt-cnt][num]!=-1)
return dp[pos][tcnt-cnt][num];
uint32_t ret=0;
int mx=up?upr[pos]:1;
for(int i=0;i<=mx;i++)
ret+=dfs(pos-1, cnt+i, i, upr, up&&i==upr[pos], tcnt);
if(!up) dp[pos][tcnt-cnt][num]=ret;
return ret;
}
uint32_t count_bit(uint32_t l, uint32_t r, int k)
{
uint_t bl=l-1, br=r;
if(!l) return dfs(31, 0, 0, br, 1, k);
return dfs(31, 0, 0, br, 1, k)-dfs(31, 0, 0, bl, 1, k);
}
void solve()
{
int64_t l, r, k;
cin>>l>>r>>k;
if((!l||!r)&&k==1) return cout<<0, void();
uint32_t L=l, R=r;
if(l<0&&!R) R=UINT32_MAX, k--;
if(r>0&&!L) L++, k--;
int s=1;
for(;;s++)
{
uint32_t v=count_bit(L, R, s);
if(v>=k) break;
k-=v;
}
l=L-1, r=R;
while(l+1<r)
{
uint32_t mid=(l+r)>>1;
if(count_bit(L, mid, s)<k) l=mid;
else r=mid;
}
cout<<(int)r<<'\n';
}
int main()
{
memset(dp, -1, sizeof dp);
int t;
cin>>t;
while(t--) solve();
}

本文作者:Jimmy-LEEE

本文链接:https://www.cnblogs.com/redacted-area/p/18379562

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起