【分治】[2016"百度之星" - 初赛(Astar Round2A)]BD String
题目
Problem Description
众所周知,度度熊喜欢的字符只有两个:B和D。
今天,它发明了一种用B和D组成字符串的规则:
S(1)=B
S(2)=BBD
S(3)=BBDBBDD
…
S(n)=S(n−1)+B+reverse(flip(S(n−1))
其中,reverse(s)指将字符串翻转,比如reverse(BBD)=DBB,flip(s)指将字符串中的B替换为D,D替换为B,比如flip(BBD)=DDB。
虽然度度熊平常只用它的电脑玩连连看,这丝毫不妨碍这台机器无与伦比的运算速度,目前它已经算出了S(21000)的内容,但度度熊毕竟只是只熊,一次读不完这么长的字符串。它现在想知道,这个字符串的第L位(从1开始)到第R位,含有的B的个数是多少?
Input
第一行一个整数T,表示T(1≤T≤1000) 组数据。
每组数据包含两个数L和R(1≤L≤R≤1018) 。
Output
对于每组数据,输出S(21000)表示的字符串的第L位到第R位中B的个数。
Sample Input
3
1 3
1 7
4 8
Sample Output
2
4
3
分析
其实
根据题目给出的构建字符串的规则,我们可以想到这很像是在构建一棵完全二叉树:
每次新建一个节点作为根,当前的树作为根的左子树,然后将左子树翻折到右边成为右子树,将右子树的B,D取反。
这个字符串就是这棵树的中序遍历序列。
我们令
将
对于每次询问
然后递归计算第一个区间,和第三个区间在其所在子树的根的左子树中所对应部分中
若
所以时间复杂度和线段树类似,每次询问问
代码
#include<cstdio>
#include<algorithm>
using namespace std;
template<class T>
void Read(T &x){
char c;
bool f=0;
while(c=getchar(),c!=EOF){
if(c=='-')
f=1;
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
if(f)
x=-x;
return;
}
}
}
long long sum[100],point[100];
long long dfs(long long l,long long r){
if(l>r)
return 0;
int pl=lower_bound(point+1,point+60+1,l)-point,pr=lower_bound(point+1,point+60+1,r)-point;
if(point[pr]>r)
pr--;
if(pl>pr)
return r-l+1-dfs(2*point[pr]-r,2*point[pr]-l);
return sum[pr-1]+1-sum[pl-1]+dfs(l,point[pl]-1)+(r-point[pr]-dfs(point[pr]-r+point[pr],point[pr]-1));
}
void prepare(){
point[1]=1;
sum[1]=1;
int i;
for(i=2;i<=60;i++){
point[i]=point[i-1]*2;
sum[i]=sum[i-1]+1+point[i]-1-sum[i-1];
}
point[61]=point[60]*2;
}
long long T;
int main()
{
prepare();
Read(T);
long long l,r;
while(T--){
Read(l),Read(r);
printf("%I64d\n",dfs(l,r));
}
}