• [贪心][预处理+二分][好题]Find the Number 2022ICPC第一场网络选拔赛D


    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.

    输入样例:

    1. 5
    2. 38 47
    3. 57 86
    4. 23 24
    5. 72 83
    6. 32 33

    输出样例:

    1. -1
    2. 68
    3. -1
    4. -1
    5. -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了也算是一个答案,也要记得判断。

    具体代码如下:

    预处理+二分

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define int long long
    9. using namespace std;
    10. vector<int> num[16];//num[i]保存所有后缀0个数为i的符合题意的数字
    11. void dfs(int x, int now, int val, int id){
    12. if(30-now < x) return;
    13. if(x == 0){
    14. num[id].push_back(val);
    15. return;
    16. }
    17. for(int i = now+1; i <= 30; i++)
    18. dfs(x-1, i, val+(1<
    19. }
    20. signed main()
    21. {
    22. for(int i = 1; i <= 15; i++){//有i个后缀0
    23. dfs(i-1, i, 1<
    24. sort(num[i].begin(), num[i].end());
    25. }
    26. int T;
    27. cin >> T;
    28. while(T--){
    29. int l, r;
    30. scanf("%lld%lld", &l, &r);
    31. for(int i = 1; i <= 15; i++){
    32. if(lower_bound(num[i].begin(), num[i].end(), l) != num[i].end()){
    33. int x = *lower_bound(num[i].begin(), num[i].end(), l);
    34. if(x <= r){
    35. printf("%lld\n", x);
    36. break;
    37. }
    38. }
    39. if(i == 15) puts("-1");
    40. }
    41. }
    42. return 0;
    43. }

     贪心

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define int long long
    8. using namespace std;
    9. void solve(){
    10. int l, r;
    11. scanf("%lld%lld", &l, &r);
    12. for(int i = 1; i <= 16; i++){//枚举0的个数
    13. int num = i-1;
    14. if(num == 0){
    15. if(l <= (1<
    16. printf("%lld\n", 1<
    17. return;
    18. }
    19. else continue;
    20. }
    21. int ans = 1<
    22. for(int j = 30; j >= i+1; j--){
    23. if(((l>>j)&1) == ((r>>j)&1)){
    24. if(((l>>j)&1) == 1){
    25. ans += 1<
    26. num--;
    27. if(num == 0){
    28. if(ans >= l && ans <= r){
    29. printf("%lld\n", ans);
    30. return;
    31. }
    32. else break;
    33. }
    34. }
    35. }
    36. else{//肯定r取1,l取0
    37. //此时第j位可以选0或1
    38. //选0时肯定小于r,不一定大于等于l,所以尽量靠左填1
    39. int cpy = ans;
    40. if(j-(i+1) >= num){//选0
    41. for(int k = j-1, cnt = 0; cnt < num; k--, cnt++)
    42. ans += 1<
    43. if(ans >= l){
    44. printf("%lld\n", ans);
    45. return;
    46. }
    47. }
    48. ans = cpy;
    49. //选1时肯定大于l,不一定小于等于r,所以尽量靠右填1
    50. if(j-(i+1) >= num-1){//选1
    51. ans += 1<
    52. for(int k = i+1, cnt = 0; cnt < num-1; k++, cnt++)
    53. ans += 1<
    54. if(ans <= r){
    55. printf("%lld\n", ans);
    56. return;
    57. }
    58. }
    59. break;
    60. }
    61. }
    62. }
    63. puts("-1");
    64. }
    65. signed main()
    66. {
    67. int T;
    68. cin >> T;
    69. while(T--) solve();
    70. return 0;
    71. }

  • 相关阅读:
    Databend 开源周报第 149 期
    贪心算法(算法竞赛、蓝桥杯)--线段覆盖
    janus-gateway安装(docker方式)(centos7)
    域名解析不生效的排查思路
    必不可少的UI组件二——组件库开发的基础知识(工程化篇)
    TinyKv介绍
    Edge在IE模式下加载网页 - Edge设置IE兼容性
    智能边缘小站 CloudPond(低延迟、高带宽和更好的数据隐私保护)
    最短路径专题2 最短距离-多终点(堆优化版)
    AIOps在业务运维的最佳应用实践
  • 原文地址:https://blog.csdn.net/m0_55982600/article/details/126918477