好数
jzoj 1521
题目大意:
定义好数为转换为二进制后,有至少三个连续的位相同的数,现在要求一个范围内的好数个数
样例输入
0 16
样例输出
5
数据范围限制
0 <= Low <= UP <= 2147483647
提示
提示:
对于50%测试,0 <= Low <= UP <= 100000。
解题思路:
我们可以用前缀和来求,就把问题转换为了前n个数中好数的个数
它让我们求好数,但因为好数特别难求,所以我们可以求‘坏数’,就是没有连续三个位是一样的数
我们先用数位DP求出多少位以什么开头的坏数总数
然后我们把他分位数小于n的位数的和等于n的位数的
小于的:
我们就可以直接用已经求出来的坏数总数,然后分为不同位数的直接加就行了
如1…1100100先分为1位,2位,3位,4位,5位,6位的类型
等于的:
就从大到小把1变成0,这样就一定小于n了,然后分别计算
最后还要计算n
然后用n减去坏数的个数就得到了好数的个数了
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
ll a,b,s[40],f[40][5][5];
ll js(ll now)
{ll sum=1;for (int i=now-1;i>0;--i)sum+=f[i][1][0]+f[i][1][1];//小于的(第一类)for (int i=now-1;i>0;--i){if (s[i]==1)if (s[i+1]==1||s[i+2]==1||i==now-1)//把1改为0sum+=f[i+1][s[i+1]][0];相加if (s[i]==s[i+1]&&s[i+1]==s[i+2]&&i!=now-1)//判断是不是已经成为了好数了{sum--;break;}}return sum;
}
ll ans(ll dep)
{if (dep<=0) return 0;ll l=dep,tot=0;memset(s,0,sizeof(s));while (l) s[++tot]=l&1,l>>=1;//转二进制return dep-js(tot);//求好数
}
int main()
{f[1][1][0]=1;f[1][0][1]=1;for (int k=2;k<=35;++k){f[k][0][0]=f[k-1][0][1];//数位DPf[k][0][1]=f[k-1][1][1]+f[k-1][1][0];f[k][1][0]=f[k-1][0][0]+f[k-1][0][1];f[k][1][1]=f[k-1][1][0];}scanf("%lld %lld",&a,&b);printf("%lld",ans(b)-ans(a-1));//前缀和求职
}