这是一道数位DP。
令 (dp_{i,j,k}) 为满足由 (i) 位组成,且其中有 (j) 个1,第 i 位(从右往左数)为 (k) 的二进制数的数量。
可以得出状态转移方程:
(dp_{i,j,0}=dp_{i-1,j,1}+dp_{i-1,j,0};(2le i,0le j< i))
(dp_{i,j,1}=dp_{i-1,j-1,0}+dp_{i-1,j-1,1};(2le i,0<jle i))
边界:(dp_{1,0,0}=1,dp_{1,1,1}=1)
对于 (dp_{i,j,k}),如果满足 (1le i,0le jle lfloor frac{i}{2} floor),则这个状态是合法的。因为0的个数为 (i-j),要满足 (jle i-j),则 (2jle i) 所以 (jle lfloor frac{i}{2} floor)
令 (f(x)) 为区间 ([1,x-1]) 内的“圆数”个数,则区间 ([L,R]) 内的“圆数”个数为 (f(R+1)-f(L))。
对于求(f(x)),我们先将 (x) 转换成二进制,设其二进制位数为 (len)。
-
将二进制位数小于 (len) 的“圆数”个数统计到答案中。这时,对于 (dp_{i,j,k}),如果满足 (1le i,0le jle lfloor frac{i}{2} floor),则这个状态是合法的。因为0的个数为 (i-j),要满足 (jle i-j),则 (2jle i) 所以 (jle lfloor frac{i}{2} floor)
-
对于 (x) 的二进制除首位外的每一位 (i),都判断其是否为1 。如果为1,说明存在一些数,它们长度为 (len),值小于 (x),且二进制表示中的前 (i-1) 位与 (x) 相同,第 (i) 位为0 。然后将这些数中的“圆数”个数加入答案即可。这时,判断一个状态是否合法,需要考虑前 (i-1)位中0和1的个数。
参考代码
略微压行,轻喷。
#include <cstdio>
#include <cstring>
using namespace std;
#define in __inline__
typedef long long ll;
#define rei register int
#define FOR(i, l, r) for(rei i = l; i <= r; ++i)
#define FOL(i, r, l) for(rei i = r; i >= l; --i)
char inputbuf[1 << 23], *p1 = inputbuf, *p2 = inputbuf;
#define getchar() (p1 == p2 && (p2 = (p1 = inputbuf) + fread(inputbuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
in int read() {
int res = 0; char ch = getchar(); bool f = true;
for(; ch < '0' || ch > '9'; ch = getchar())
if(ch == '-') f = false;
for(; ch >= '0' && ch <= '9'; ch = getchar())
res = res * 10 + (ch ^ 48);
return f ? res : -res;
}
const int N = 40;
ll dp[N][N][2];
int a, b, A[N], la, lb, B[N];
ll solve(int x[], int len) {
ll res = 0; int s0 = 0, s1 = 1;
//s0表示0的个数,s1表示1的个数
FOL(i, len - 1, 1) FOR(j, 0, (i >> 1)) res += dp[i][j][1];//第1类数
FOL(i, len - 1, 1) {//第二类数
if(x[i]) FOR(j, 0, i) if(s0 + i - j >= s1 + j) res += dp[i][j][0];
//s0+i-j表首位至当前位0的个数,s1+j表首位至当前位1的个数,注意j要枚举至i
x[i] ? (++s1) : (++s0);
}
return res;
}
signed main() {
a = read(), b = read();
for(; a; a >>= 1) A[++la] = a & 1;
for(; b; b >>= 1) B[++lb] = b & 1;//转换成二进制
++B[1];
for(rei i = 2; i <= lb && B[i - 1] == 2; ++i) B[i - 1] = 0, ++B[i];
if(B[lb] == 2) B[lb] = 0, B[++lb] = 1;
while(!A[la]) --la;
while(!B[lb]) --lb;//给B加上1
dp[1][0][0] = dp[1][1][1] = 1;
FOR(i, 2, lb) FOR(j, 0, i) {//DP
if(j < i) dp[i][j][0] = dp[i - 1][j][1] + dp[i - 1][j][0];
if(j) dp[i][j][1] = dp[i - 1][j - 1][0] + dp[i - 1][j - 1][1];
}
printf("%lld
", solve(B, lb) - solve(A, la));
return 0;//结束
}