DescriptionDescriptionDescription
把一个数x转化为二进制数后,若有连续的三个相同数字,那么x则为好数,现在问区间[l,r][l,r][l,r]中有多少个好数
InputInputInput
一行,两个整数Low、UP,其中0 <= Low <= UP <= 2147483647。
OutputOutputOutput
一个整数。
SampleSampleSample InputInputInput
0 16
SampleSampleSample OutputOutputOutput
5
HintHintHint
对于50%测试,0 <= Low <= UP <= 100000。
TrainTrainTrain ofofof ThoughtThoughtThought
对于这一道题目而言,求好数其实比坏数(没有三个相邻)要难,因此我们可以用数位dp来求解
设f[i][s[i]][s[i−1]]f[i][s[i]][s[i-1]]f[i][s[i]][s[i−1]]为前i段字符串中以i和i-1位为开头的坏数个数
则有以下方程
a[i][0][0]=a[i-1][0][1];
a[i][0][1]=a[i-1][1][0]+a[i-1][1][1];
a[i][1][0]=a[i-1][0][0]+a[i-1][0][1];
a[i][1][1]=a[i-1][1][0];
第一个,不能有连续且相同的三位
第四个同理
第二第三个,因为没有连续的,所以0 or 1都可以
CodeCodeCode
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
bool b;
int s[35],a[35][2][2],low,up,ans,l;
int dp(int x)
{
memset(s,127/3,sizeof(s));
if (x<=0) return 0;
int xx=x;
int p=0;
while (x!=0)
{
p=x%2;
s[++l]=p;
x/=2;
}//转二进制
for (int i=1; i<=(l+1)/2; ++i)
swap(s[i],s[l-i+1]);
a[1][0][0]=a[1][1][0]=a[1][0][1]=a[1][1][1]=1;
a[2][1][0]=a[2][0][1]=a[2][1][1]=a[2][0][0]=1;//定义初值
for (int i=3; i<=31; ++i)
{
a[i][0][0]=a[i-1][0][1];
a[i][0][1]=a[i-1][1][0]+a[i-1][1][1];
a[i][1][0]=a[i-1][0][0]+a[i-1][0][1];
a[i][1][1]=a[i-1][1][0];
}
ans=1;
for (int i=2; i<l; ++i)
ans+=a[i][1][0]+a[i][1][1];//计算坏数和
for (int i=2; i<=l; ++i)
{
if (s[i]==1) {
s[i]=0;
for (int j=i; j>=3; --j)
if (s[j]==s[j-1] && s[j]==s[j-2]) b=true;
if (!b) ans+=a[l-i+2][s[i-1]][0];//判断长度一样但是大小不及up的
b=false;
s[i]=1;
}
}
for (int i=1; i<=l; ++i)
if (s[i]==s[i+1] && s[i]==s[i+2]) b=true;//判断本身是不是坏数
if (!b && l>1) ans++;
memset(a,0,sizeof(a));
memset(s,127/3,sizeof(s));
b=false; l=0;
return xx-ans;
}
int main()
{
// freopen("d.in","r",stdin);
// freopen("d.out","w",stdout);
scanf("%d%d",&low,&up);
printf("%d",dp(up)-dp(low-1));
return 0;
}