【GDOI2018模拟7.6】吃干饭

Description

求区间[l,r]中的数任意互相异或之后有多少种可能的结果
l<=r<=1e18,数据组数<=100

Solution

这种题一眼线性基啦~
虽然我并不太熟练,还自己推了一遍插入
这样直接暴力做有50分
然后打了个表发现了一个规律:
首先l一定会在线性基中,
然后我们知道了线性基中的最后一个数,想要快速找出线性基中的下一个数
假设当前的数的二进制表示是 10100,那么接下来的会是
10100
10101
10110
11000
11100
也就是每次找当前最后一个数中,从最低位起第一个当前位为0且线性基为空的位置
然后这一位是1,后面就都是0,并插入到这一位的线性基中
感性理解一下好像没什么毛病
那就直接模拟这个过程就好了,复杂度O(T log^2 L)

Code

#include 
#include 
#include 
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define fd(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
typedef long long ll;
ll l,r,ans,a[61],mi[61];
int main() {
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    int ty;
    mi[0]=1;fo(i,1,60) mi[i]=mi[i-1]*2;
    for(scanf("%d",&ty);ty;ty--) {
        scanf("%lld%lld",&l,&r);
        if (!l&&!r) {
            printf("1\n");
            continue;
        }
        fo(i,0,60) a[i]=0;
        ll x=l;ans=2;if (!x) x=1;
        fd(i,60,0) if (x&mi[i]) {a[i]=x;break;}
        while (xint now=61;
            fo(i,0,60)
                if (!a[i]&&!(x&mi[i])) {
                    now=i;
                    break;
                }
            if (now>60) break;
            fo(i,0,now-1) if (x&mi[i]) x-=mi[i];
            x+=mi[now];a[now]=x;
            if (x<=r) ans=ans*2;
        }
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(线性基,位运算)