http://acm.hdu.edu.cn/showproblem.php?pid=5694
衆所周知,度度熊喜歡的字符只有兩個:B和D。
今天,它發明了一種用B和D組成字符串的規則:
S(1)=BS(1)=B
S(2)=BBDS(2)=BBD
S(3)=BBDBBDDS(3)=BBDBBDD
…
S(n)=S(n−1)+B+reverse(flip(S(n−1))S(n)=S(n−1)+B+reverse(flip(S(n−1))
其中,reverse(s)reverse(s)指將字符串翻轉,比如reverse(BBD)=DBBreverse(BBD)=DBB,flip(s)flip(s)指將字符串中的BB替換爲DD,DD替換爲BB,比如flip(BBD)=DDBflip(BBD)=DDB。
雖然度度熊平常只用它的電腦玩連連看,這絲毫不妨礙這臺機器無與倫比的運算速度,目前它已經算出了S(21000)S(21000)的內容,但度度熊畢竟只是只熊,一次讀不完這麼長的字符串。它現在想知道,這個字符串的第LL位(從1開始)到第RR位,含有的BB的個數是多少?
Input
第一行一個整數TT,表示T(1≤T≤1000)T(1≤T≤1000) 組數據。
每組數據包含兩個數LL和R(1≤L≤R≤1018)R(1≤L≤R≤1018) 。
Output
對於每組數據,輸出S(21000)S(21000)表示的字符串的第LL位到第RR位中BB的個數。
Sample Input
3 1 3 1 7 4 8
Sample Output
2 4 3
首先用a[i] 存儲第i個字符串的長度, 因爲l ,r 小於10^18, 所以只需要開到a[60] 就可以了 (2^60 > 10^18)
get(x) 求 1 ~ x中B的個數
舉個例子:
第一個紅框中B的個數爲c, 第二個爲a, 第三個爲b;
如果我要求箭頭處(x)的前綴的B的個數 sum = c + a + 1 + b
因爲取反再反轉,所以 a和b是互補的, 所以 a 和 b 中 B 的個數 等於 len(a), 也就是a的長度
而 len(a) = x - a[3] - 1 , 加上中間的一個B,可以抵消 - 1
c中B的個數就用遞歸求
所以 get(x) = get(a[i] - x ) + (x - a[i] - 1 ) + 1 == x - a[i-1] + get(a[i] - x);
#include <bits/stdc++.h>
using namespace std;
using lon = long long;
lon a[65];
lon get(lon x)
{
if(x == 0) return 0;
for(int i = 1; i <= 60; i++)
{
if(a[i] == x)
return x / 2 + 1;
if(x < a[i])
return x - a[i-1] + get(a[i] - x);
}
}
int main()
{
for(int i = 1; i <= 60; i++)
a[i] = a[i-1] * 2 + 1;
int T;
cin >> T;
while(T--)
{
lon l, r;
cin >> l >> r;
// cout << get(l-1) << endl;
cout << get(r) - get(l-1) << endl;
}
return 0;
}