题目链接:HDU5694
BD String
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 510 Accepted Submission(s): 225
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 的个数是多少?
今天,它发明了一种用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) 。
每组数据包含两个数 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
题目分析:我记得去年比赛貌似做过这道题,当时没有做出来,后来看评论区才知道是去年CCPC热身赛的题目。现在都还是这么弱,当时跟连新手村都没出差不多了。这题回头看一下发现还是挺水的,说是2的1000次方,实际最多也只能取2的60几次方。考虑每个新的增加的串都是由原来的串得来的,加上10的18次方数据量基本肯定是递归O(logn)算法无误了。
我们可以分别把1到L和1到R的B个数都求出来,然后相减。以R为例,把前面16个或者32个写出来数数基本规律也就出来了,前2^m有2^(m-1)个B,还要加上第2^m的B。这里注意要取恰好比R大的那个次方数,然后减去R到2^m的B的个数,不考虑BD互换的话这个串是原串前后翻转得到的,加上BD互换,因此只需求1到(2^m-R+1)D的个数,这里也就是为什么要取恰好比R大的原因了。这样依次递归的向前求解即可。时间复杂度O(logn)。注意数很大,要用unsigned long long(貌似)。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
unsigned long long int two[1001],p[1001];
int T;
unsigned long long int l,r;
unsigned long long int slove(unsigned long long int x)
{
if(x==0 ) return 0;
if(x==1) return 1;
if(x==2||x==3) return 2;
unsigned long long int ans=0;
for(int i=0;i<64;i++)
{
if(two[i]==x)
return p[i]+1;
if(two[i]<x&&two[i+1]>x)
{
ans+=p[i+1];
ans-=(two[i+1]-x-1-slove(two[i+1]-x-1));
return ans;
}
}
return 0;
}
int main()
{
two[0]=1;
for(int i=1;i<64;i++)
{
two[i]=2*two[i-1];
p[i]=two[i-1];
}
scanf("%d",&T);
while(T--)
{
cin>>l>>r;
l-=1;
long long int ans=slove(r)-slove(l);
cout<<ans<<endl;
}
}