Let's call a number x good if x is an interger and ctz(x)=popcount(x).
ctz(x) is the number of trailing zeros in the binary representation of x.
popcount(x) is the number of 1's in the binary representation of x.
For example:
ctz(12)=ctz(11002)=2 and popcount(12)=popcount(11002)=2, so 12 is a good number.
Now you are given an interval [l,r], you need to find a good number in it. If there are multiple solutions, print any of them; if there is no solution, print −1 instead.
The first line contains a single integer T (1≤T≤105) --- the number of test cases.
For each test case, there is one single line containing two integers l,r (1≤l≤r≤109) --- the interval.
For each test case, print one line containing the answer.
If there are multiple solutions, print any of them; if there is no solution, print −1 instead.
- 5
- 38 47
- 57 86
- 23 24
- 72 83
- 32 33
- -1
- 68
- -1
- -1
- -1
题意: 给出区间[l, r],求在该区间内有多少数字的二进制表示满足后缀0个数等于全部1个数。
分析: 由于满足题意的数字并不是特别多,大概是5e5左右个,当时想到了打表,但显然是没法把这些数字全部放进代码里的,所以当时这条路就没走下去,但赛后发现完全可以把这些数字暴力求出来,然后放入15个vector中,暴搜的复杂度很低,大概也是5e5级别的,当求出符合题意的全部数字后这道题就很简单了,对于每个询问直接找出第一个大于等于l的数字,然后看它是否小于等于r,不得不说这种思路确实很强。
赛时想的是另一种贪心的思路,首先枚举后缀零个数,假设后缀零有i个,然后考虑l和r的二进制表示,从第30位到第0位枚举。而答案应该出现在二者中间,如果设二者最早不同的位置为t的话,那么从第30位到第t+1位的位置x取值是唯一的,上下限都被l和r卡死了。而r的第t位肯定是1,l的第t位肯定是0,此时x可以选择取0或者是1。如果x取0那么最终值肯定小于r,但是并不一定大于等于l,所以此时贪心地想剩余的1的位置一定是连续地放在第t-1位及其后面的位置。如果x取1那么最终值一定大于l,但是同样不保证它小于等于r,所以贪心地想剩余的1一定放在i+1位及其往前的位置。最后连续放1的时候需要判断下空间是否够放,如果不够放那说明这样是构不成答案的。在一开始被l和r卡死的位置也要注意动态更新剩余1的个数,同时这时候如果剩余1个数减到0了也算是一个答案,也要记得判断。
具体代码如下:
预处理+二分
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define int long long
- using namespace std;
-
- vector<int> num[16];//num[i]保存所有后缀0个数为i的符合题意的数字
-
- void dfs(int x, int now, int val, int id){
- if(30-now < x) return;
- if(x == 0){
- num[id].push_back(val);
- return;
- }
- for(int i = now+1; i <= 30; i++)
- dfs(x-1, i, val+(1<
- }
-
- signed main()
- {
- for(int i = 1; i <= 15; i++){//有i个后缀0
- dfs(i-1, i, 1<
- sort(num[i].begin(), num[i].end());
- }
- int T;
- cin >> T;
- while(T--){
- int l, r;
- scanf("%lld%lld", &l, &r);
- for(int i = 1; i <= 15; i++){
- if(lower_bound(num[i].begin(), num[i].end(), l) != num[i].end()){
- int x = *lower_bound(num[i].begin(), num[i].end(), l);
- if(x <= r){
- printf("%lld\n", x);
- break;
- }
- }
- if(i == 15) puts("-1");
- }
- }
-
- return 0;
- }
贪心
- #include
- #include
- #include
- #include
- #include
- #include
- #define int long long
- using namespace std;
-
- void solve(){
- int l, r;
- scanf("%lld%lld", &l, &r);
- for(int i = 1; i <= 16; i++){//枚举0的个数
- int num = i-1;
- if(num == 0){
- if(l <= (1<
- printf("%lld\n", 1<
- return;
- }
- else continue;
- }
- int ans = 1<
- for(int j = 30; j >= i+1; j--){
- if(((l>>j)&1) == ((r>>j)&1)){
- if(((l>>j)&1) == 1){
- ans += 1<
- num--;
- if(num == 0){
- if(ans >= l && ans <= r){
- printf("%lld\n", ans);
- return;
- }
- else break;
- }
- }
- }
- else{//肯定r取1,l取0
- //此时第j位可以选0或1
- //选0时肯定小于r,不一定大于等于l,所以尽量靠左填1
- int cpy = ans;
- if(j-(i+1) >= num){//选0
- for(int k = j-1, cnt = 0; cnt < num; k--, cnt++)
- ans += 1<
- if(ans >= l){
- printf("%lld\n", ans);
- return;
- }
- }
- ans = cpy;
- //选1时肯定大于l,不一定小于等于r,所以尽量靠右填1
- if(j-(i+1) >= num-1){//选1
- ans += 1<
- for(int k = i+1, cnt = 0; cnt < num-1; k++, cnt++)
- ans += 1<
- if(ans <= r){
- printf("%lld\n", ans);
- return;
- }
- }
- break;
- }
- }
- }
- puts("-1");
- }
-
- signed main()
- {
- int T;
- cin >> T;
- while(T--) solve();
- return 0;
- }
-
相关阅读:
Databend 开源周报第 149 期
贪心算法(算法竞赛、蓝桥杯)--线段覆盖
janus-gateway安装(docker方式)(centos7)
域名解析不生效的排查思路
必不可少的UI组件二——组件库开发的基础知识(工程化篇)
TinyKv介绍
Edge在IE模式下加载网页 - Edge设置IE兼容性
智能边缘小站 CloudPond(低延迟、高带宽和更好的数据隐私保护)
最短路径专题2 最短距离-多终点(堆优化版)
AIOps在业务运维的最佳应用实践
-
原文地址:https://blog.csdn.net/m0_55982600/article/details/126918477