题目连接
题意
- 给我们一个数 n,让求 0~n 中所有数的连续 1 的数量和。
某个数的连续 1 的数量和,这个数的相邻 bit 位都是 1,那么连续 1 数量 + 1
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f174bb01206804dd390aefd4d84a4a98.png)
思路
- 老数位 dp 了,
- 这题一开始 dfs 记忆化 dp [pos][pre] , 只开了两维,是无法完全记录状态的 (
是因为当前这个 dp 状态不仅受 1~pos 位中 连续 1 的数量影响,还受 pos+1 到 p位之间的连续 1 影响!!!
,p 为 当前某个数二进制位的位数),所以求的方案总是少… - 因此我们把 dp 数组开成 dp [pos][num][pre], pos 当前 dfs 到那一层了,num 为 pos+ 1 ~ p 中连续 1 对答案的贡献,pre 为前一个 bit 为是 0 还是 1.
代码
#include <bits/stdc++.h>
using namespace std;
#define db double
#define ll long long
#define sc scanf
#define pr printf
#define fi first
#define se second
#define pb push_back
#define m_p make_pair
#define Pir pair<int, int>
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
const int N = 35;
int a[N];
ll dp[N][N][3];
int b[N];
ll dfs(int pos, int lim, int pre, ll num)
{
if (! lim && dp[pos][num][pre] != -1) return dp[pos][num][pre];
if (pos == 0) return num;
int all = lim ? a[pos] : 1;
ll ans = 0;
for (int i = 0; i <= all; i ++)
{
ans += dfs(pos - 1, lim && (i == all), i, num + (pre && i));
}
if (! lim) dp[pos][num][pre] = ans;
return ans;
}
ll sov(int n)
{
int p = 0;
while (n)
{
a[++ p] = n % 2;
n /= 2;
}
return dfs(p, 1, 0, 0);
}
int main()
{
memset(dp, -1, sizeof dp);
int T, cas = 1; sc("%d", &T);
while (T --)
{
int n; sc("%d", &n);
pr("Case %d: %lld\n", cas ++, sov(n));
}
return 0;
}