题意:
按照题目中的规则构造字符串。
思路:
最开始第一眼被题目的数据吓到了,2^1000,根本不知道从哪儿入手。看了题解之后发现没有那么夸张,因为L,R都是在LL的范围内的,所以找到规律的话LL一定是足够的。因为是按照题目的规则构造字符串,所以要把性质给挖掘出来才有做的可能。
1. 第n个串的长度Len[n]为2^n-1;
2. 第2^n位上的字符一定是B;
3. 第n个串包含3个部分:前面2^(n-1),B,后面2^(n-1);其中前面部分包含的B的个数等于后半部分包含的D的个数,可以推出第n个串包含2^(n-1)+1个B;
4. 同理,任意取i,2^n+i与2^n-i关于2^n对称,一个D对应一个B。
以上:
问题要求我们求出L到R的B的个数,可以转化为求出0-R中B的个数减去0-L-1中B的个数,因为依据性质从0开始去计算更方便。
对于任意从0开始的长度为a的串,包含三个部分:前2^x-1,B,后a-2^x;
从大到小枚举x,则对于每一个串都有(a-2^x)<(2^x-1),则我们可以确定得到(a-2^x+1)个B,依据性质3,4。
同时更新a的长度为2^(x+1)-1-a。
很巧妙的替换,一道好题。
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define LL long long
using namespace std;
LL T;
LL L,R;
LL calculate(LL a);
int main()
{
scanf("%I64d",&T);
while( T-- ){
scanf("%I64d %I64d",&L,&R);
printf("%I64d\n",calculate(R)-calculate(L-1));
}
return 0;
}
LL calculate(LL a){
LL res = 0;
for( int i = 62; i >= 0; i-- ){
//注意这个1默认类型的越界
LL len = (LL)1<<i;
if( a >= len ){
res += a-len+1;
a = (len<<1)-1-a;
}
}
return res;
}