您的位置:首页 > 其它

【GDOI2018模拟7.6】吃干饭

2017-07-06 21:55 369 查看

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 <cstdio>
#include <cstring>
#include <algorithm>
#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 (x<r) {
int 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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: