HDU

http://acm.hdu.edu.cn/showproblem.php?pid=5969 (合肥)区域赛签到题。。。orz

题意:给你l,r,求x|y的max,x,y满足l<=x<=y<=r.

题解:初始想法是找到形如(111,1000)、(11111,100000)····这样的进位点,一旦区间里有这种相邻两位进位的或一下就可以得到1111····,必然最大。(后面的是瞎搞)如果区间里没有,说明l,r二进制长度一样,于是从最高位往下找,一直找到不一样的一位,变成了前一种有进位的情况。这个算法的正确性完全没有证明,但是头铁。。结果一直wa。

正确的想法是对于二进制下r的每个0,去找在l,r区间是否存在一个数K可以把这个0变成1,顺便把剩下所有的数变成1.如果可以就输出变化后的数。

算法是从r的二进制最高位开始往底扫,遇到一个0,就找一个最大的数k,使得k|r将这一位的0变成1。然后判断一下k是否大于 l 。

找k的方法是将从r的第一个0开始的将其后面的数组全部变为0,然后-1,直观地看就是将第一个0前面的1变成0,后面全部变成1.

保存r二进制用一个数组,清零的时候维护一个sum简化操作。

ac代码

#define    _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
typedef long long ll;
ll l, r;
ll pow(ll x) {
    if (x == 0)return 1;
    
    ll s = 1;
    while (x--) {
        s <<= 1;
    }
    return s;
}
ll ans[5005];

int main(){
    int t;
    cin >> t;
    while (t--) {
        cin >> l >> r;
        ll sum=0;
        ll bit = 0,  rr = r;
        int cnt = 0;
        while (rr) { ans[cnt++] = (rr & 1); rr >>= 1; }

        for(int i=0;i<cnt;i++){
            if (ans[i] == 0) {
                ll k = r - sum - 1;
                if (k >= l)ans[i] = 1;
            }
            else { sum += pow(i); }
        }
        sum = 0;
        for (int i = 0; i < cnt; i++){ sum += pow(i)*ans[i]; }
        cout << sum << endl;

    }
}
成功的路并不拥挤,因为大部分人都在颓(笑)
原文地址:https://www.cnblogs.com/SuuT/p/8551584.html