首页 > 其他分享 >题解:SP1182 SORTBIT - Sorted bit squence

题解:SP1182 SORTBIT - Sorted bit squence

时间:2024-08-25 21:04:54浏览次数:6  
标签:int 题解 SP1182 个数 二进制 squence tcnt 排序 uint32

题意

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

其中,负数使用补码来表示:一个负数的二进制数与其相反数的二进制数之和恰好等于 \(2^{32}\)。

分析

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

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


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

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

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

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

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


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

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

int 下输出这个数即可。


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

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

时间复杂度 \(O(T\log^3n)\)。

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

标签:int,题解,SP1182,个数,二进制,squence,tcnt,排序,uint32
From: https://www.cnblogs.com/redacted-area/p/18379562

相关文章

  • 题解:P3266 [JLOI2015] 骗我呢
    题意有一个\(n\timesm\)的数组\(x_{i,j}(1\lei\len,1\lej\lem)\),满足:\(x_{i,j}\in[0,m]\)\(\foralli\in[1,n],\forallj\in[1,m),x_{i,j}<x_{i,j+1}\)\(\foralli\in(1,n],\forallj\in[1,m),x_{i,j}<x_{i-1,j+1}\)......
  • 题解:CF70D Professor's task
    题意实现以下两种操作:往点集\(S\)中添加一个点\((x,y)\)。询问点\((x,y)\)是否在点集\(S\)的凸包中。分析动态凸包板子。建议先完成P2521[HAOI2011]防线修建。上题维护的是上半个凸包,本题维护上下两个。将凸包中的点按\(x\)排序,通过\((x,y)\)前驱......
  • 题解:P2521 [HAOI2011] 防线修建
    题意给定若干个点,实现下列操作:删除一个点。查询上凸包的周长。分析建议先完成【模板】二维凸包。对于这个删除操作,我们没有好的方法去在线维护它。考虑离线询问。这样删除操作就变成了插入操作。插入一个新点有如下两种情况:新点在凸包内。新点在凸包外。我们回忆......
  • 题解:CF235C Cyclical Quest
    题意给定一个主串\(S\)和\(n\)个询问串,求每个询问串的所有循环同构在主串中出现的次数总和。分析后缀自动机好题。循环同构的过程可以看作从该串的头部删除一个字符,并在尾部加入一个字符。在后缀自动机上,跳parent树的过程就相当于删除头部的若干个字符。所以我们可......
  • 7z解压crc错误_7-Zip - 常见问题解答
    7z解压crc错误_7-Zip-常见问题解答7z解压crc错误_7-Zip-常见问题解答1.引言1.17-Zip简介1.2CRC错误概述2.7z文件和CRC错误2.1什么是7z文件2.2CRC错误的定义2.3CRC错误对文件的影响3.常见原因分析3.1文件传输过程中的错误3.2存储介质的损坏3.3不兼容的压......
  • 题解:P5680 [GZOI2017] 共享单车
    题目分析出题人是擅长隐藏题意的建树首先给你一张无向图,然后指定一个根节点\(k\),从根节点开始沿最短路到每一个节点。如果到某个节点有多条最短路径,选择上一个节点编号最短的。考虑记录前驱的Dijkstra。namespaceDJ{intdis[maxn],pre[maxn],val[maxn],vis[maxn]......
  • 题解:SP22382 ETFD - Euler Totient Function Depth
    题目链接:link,点击这里喵。前置知识:【模板】线性筛素数,欧拉函数,点击这里喵。题意简述:给定整数$l,r,k$,求出$[l,r]$中有多少个整数不断对自己取欧拉函数刚好$k$次结果为$1$。思路:看眼数据范围,$10^{10}$的量级显然不容我们每次暴力,故考虑预处理$\varphi(i),can(i,k),su......
  • 【面试系列】大数据平台常见面试题解答
    欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏:工......
  • AtCoder ABC 368题解
    前言本题解部分思路来自于网络。A-Cut题目大意有\(n\)张卡片叠在一起,从上到下给出\(n\)卡片的编号,将\(k\)张卡片从牌堆底部放到顶部后,从上到下输出卡片的编号。解题思路按照题意模拟即可。code#include<bits/stdc++.h>usingnamespacestd;inta[105];intmai......
  • SP666 VOCV - Con-Junctions 题解
    注意到这个问题具有最优子结构性,考虑树上dp。记$dp[i][0/1]$表示i号节点不放灯或放灯的最小值,$s[i][0/1]$为对应的方案数。那么我们可以进行如下转移:$dp[u][0]=\sum_{u->v}dp[v][1]$$dp[u][1]=\sum_{u->v}\min(dp[v][0],dp[v][1])$在更新对应的dp数组时,我们用......