DescriptionDescriptionDescription
某一个数的二进制中有三个连续相同的数为好数
例如
8的二进制为1000
有三个连续的0
7的二进制为111
有三个连续的1
给定n与m
求在n与m之间的好数个数
InputInputInput
一行,n与m
OutputOutputOutput
一行,这样的数的个数
SampleInputSample InputSampleInput#1
0 16
SampleInputSample InputSampleInput#2
17 100
SampleInputSample InputSampleInput#3
2000000000 2100000000
SampleInputSample InputSampleInput#4
2 5
SampleOutputSample OutputSampleOutput#1
5
SampleOutputSample OutputSampleOutput#2
49
SampleOutputSample OutputSampleOutput#3
100000001
SampleOutputSample OutputSampleOutput#4
0
思路
数位DP
题目中求好数
但我们也可以求坏数
我们设F[i][0][0]F[i][0][0]F[i][0][0]
为第iii位(从右往左)为0
与第i−1i-1i−1位(从右往左)为0
的二进制的坏数的个数
二进制:
10−1110-1110−11为F[2][1][0]+F[2][1][1]F[2][1][0]+F[2][1][1]F[2][1][0]+F[2][1][1]
100−111100-111100−111为F[3][1][0]+F[3][1][1]F[3][1][0]+F[3][1][1]F[3][1][0]+F[3][1][1]
100−111100-111100−111为F[4][1][0]+F[4][1][1]F[4][1][0]+F[4][1][1]F[4][1][0]+F[4][1][1]
100−111100-111100−111为F[5][1][0]+F[5][1][1]F[5][1][0]+F[5][1][1]F[5][1][0]+F[5][1][1]
转移:
F[i][1][0]=F[i−1][0][0]+F[i−1][0][1]F[i][1][0]=F[i-1][0][0]+F[i-1][0][1]F[i][1][0]=F[i−1][0][0]+F[i−1][0][1]
F[i][0][1]=F[i−1][1][1]+F[i−1][1][0]F[i][0][1]=F[i-1][1][1]+F[i-1][1][0]F[i][0][1]=F[i−1][1][1]+F[i−1][1][0]
如果是两个0或1
就只能这样
F[i][1][1]=F[i−1][1][0]F[i][1][1]=F[i-1][1][0]F[i][1][1]=F[i−1][1][0]
F[i][0][0]=F[i−1][0][1]F[i][0][0]=F[i-1][0][1]F[i][0][0]=F[i−1][0][1]
因为如果还是F[i−1][0][0]F[i-1][0][0]F[i−1][0][0]或F[i−1][1][1]F[i-1][1][1]F[i−1][1][1]
那就是好数了
假设mmm为100
二进制为10101010
按照上面的方法
我们能求出
1−111111111-111111111−11111111中的坏数
那10000000-10101010中的坏数怎么求呢
我们可以从左往右
除去第一位的1
一个个变0求
例如10101010
除去第一位的1
第二位的1变0
就变成了100???
这样就是F[7][0][0]F[7][0][0]F[7][0][0]
第三位的1变0
就变成了10100???
这样就是F[5][0][0]F[5][0][0]F[5][0][0]
接着第四位
1010100?
F[3][0][0]F[3][0][0]F[3][0][0]
三个加起来就是1000000-10101010中的坏数
最后在看一下m是否为坏数
就能求出0到m之间的坏数了
(n-1)同理
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
ll F[125][2][2];
ll Ans,Sum,len,k,n,m;
string sm;
ll Change(ll t)
{
if(t<=0)return 0;
Ans=0,k=t,len=0;
sm="";
while(k)//求t的二进制
{
char s=k%2+48;
sm=s+sm,k/=2;
}
len=sm.length();
for(int i=2;i<len;++i)//求t的二进制长度-1位的坏数
Ans+=F[i][1][0]+F[i][1][1];
if(t>0)Ans++;//0也是坏数
k=0;//求t的二进制长度位的坏数
while(++k<=len)
if(sm[k]=='1')
{
sm[k]='0';
int l=1;char r=sm[0];
bool B=0;
for(int i=1;i<=k;++i)
if(sm[i]==r)
{
l++;
if(l==3){B=1;break;};
}
else l=1,r=sm[i];
if(B==0)
Ans+=F[len-k+1][sm[k-1]-48][0];
sm[k]='1';//记得变回来
}
int l=1;char r=sm[1];
bool B=0;
for(int i=2;i<=len;++i)//判断m是否为坏数
if(sm[i]==r)
{
l++;
if(l==3){B=1;break;};
}
else l=1,r=sm[i];
if(!B)Ans++;
return t-Ans;
}
int main()
{
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&n,&m);
F[2][1][0]=F[2][1][1]=
F[2][0][0]=F[2][0][1]=1;//初值
for(int i=3;i<=31;++i)
{
F[i][0][0]=F[i-1][0][1];
F[i][1][0]=F[i-1][0][0]+F[i-1][0][1];
F[i][1][1]=F[i-1][1][0];
F[i][0][1]=F[i-1][1][1]+F[i-1][1][0];
}
Sum=Change(m)-Change(n-1);//前缀和
printf("%d",Sum);
return 0;
}

这篇博客介绍了一种使用动态规划(DP)解决寻找二进制中具有三个连续相同数字的好数问题的方法。通过求解坏数(不满足条件的数)的数量,博主详细阐述了状态转移方程,并提供了思路分析,包括如何处理边界情况和求解特定范围内的好数个数。

492

被折叠的 条评论
为什么被折叠?



