CF-gym/101810 J、T-Shirts Dilemma

题目链接:点我

 

题意:

给你一个区间[a,b],让你从里面选一个连续子区间[x,y](子区间可以为[a,b]),把这个区间的所有数或起来x|x+1|x+2|...|y

你要使得区间[x,y]异或起来的结果要小于等于v。让你输出这个子区间的最大长度

 

题解:

我们可以先不考虑题目这个问题,换一个简单的问题:

对于任意一个数num,找到一个最大的x,xnum,使其x|(x+1)|(x+2)||num>num

结论:

如果存在这样一个数x,那么num的二进制表示中的最低位的一个0,求这个0如果变成1会增加权值ans,然后x=num-ans

 

例如num二进制为:110101,那么x=110011

这个时候x|x+1|x+2|...|num刚好大于num,如果从x+1|x+2|...num就不会大于num(可以试试

 

那么可以说对于可选区间[x,y],如果把y=num,那么可选区间最长长度就是num-x

 

那么简化的问题就解决了,对于题目也就相当于可选区间[x,y]的右边界y不一定等于v,那么我们就可以枚举所有小于v的值,然后从中取最大区间就可以了

因为我们解决问题的时候求的x是num减去num的最低位二进制表示最低位0的权值,那么对于区间[x,y]右边界y的选取可以枚举0的位置就可以了

 

我们可以使用lowbit(~a)方式求num的最低位二进制表示最低位0的权值

因为~a就是把a按照二进制形式每一位取反,这样的话每一位的0变成了1,1变成了0

然后使用lowbit(b)方法求出来b的二进制下最低位1的权值

x&(-x),当x为0时结果为0;x为奇数时,结果为1;x为偶数时,结果为x中2的最大次方的因子。

而且这个有一个专门的称呼,叫做lowbit,即取2^k。

代码:

#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <bitset>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int readint()
{
    int tmp = 0, fh = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-') fh = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar();
    return tmp * fh;
}
inline long long readll()
{
    long long tmp = 0, fh = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-') fh = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar();
    return tmp * fh;
}
ull lowbit(ull x) //求一个数二进制中的最低位1的数值
{
    return -x & x;
}
ll min(ll a,ll b)
{
    if(a<b) return a;
    else return b;
}
ll max(ll a,ll b)
{
    if(a>b) return a;
    else return b;
}
int main()
{
    //printf("%lld
",1ll<<62);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll a,b,v,len1=0,len2=0;
        scanf("%lld%lld%lld",&a,&b,&v);
        for(int i=0;i<=62;++i) //1左移62位都1e19呢
        {
            if((1ll<<i)&b)
                len1=i;
            if((1ll<<i)&v)
                len2=i;
        }
        if(len2>len1)
        {
            printf("%lld
",b-a+1);
            continue;
        }
        if(v<a)
        {
            printf("0
");
            continue;
        }
        ll ans=0;
        ull res=v;
        while(1)
        {
            ull l;  //因为我们要求出来res的最低二进制位0的权值,所以需要取反
            ull tmp=lowbit(~res);
            if(res>=tmp)
            {
                l=res-tmp;
            }
            else l=a-1;
            ans=max(ans,min(res,b)-max(l,a-1));
            printf("%lld***
",ans);
            res=l;
            if(a>res) break;
        }
        printf("%lld
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/13930316.html