洛谷P6218 [USACO06NOV] Round Numbers S 题解 数位DP

题目链接:https://www.luogu.com.cn/problem/P6218

题目大意:
\([l,r]\) 范围内有多少数满足二进制表示中的 \(0\) 的数量大于等于 \(1\) 的数量。

解题思路:
数位DP,定义状态 \(f[p][z][x]\) 表示:

  • 当前在第 \(p\) 位;
  • \(z\) 对应目前是否一直是前置零(true为一直是,false为不是);
  • \(x\) 表示 \(0\) 的数量减去 \(1\) 的数量(因为在实现时可能存在 \(x\) 是负数所以实现的时候加了一个 \(33\) 的偏移)。

示例程序如下:

#include <bits/stdc++.h>
using namespace std;
// f[p][z][x]表示第p为,z=0个数-1个数,x
int f[33][2][66], a[33], tmp;
bool vis[33][2][66];
int dfs(int p, bool z, int x, bool limit) {
    if (p == -1) {
        return x >= 0 ? 1 : 0;
    }
    if (!limit && vis[p][z][x+33]) return f[p][z][x+33];
    int res = 0, up = limit ? a[p] : 1;
    for (int i = 0; i <= up; i ++) {
        tmp += (1<<p) * i;
        bool zz = z && i==0 && p;
        int xx = x;
        if (i == 1) xx --;
        else if (!zz) xx ++;
        res += dfs(p-1, zz, xx, limit && i==up);
        tmp -= (1<<p) * i;
    }
    if (!limit) {
        vis[p][z][x+33] = true;
        f[p][z][x+33] = res;
    }
    return res;
}
int handle(int num) {
    int p = 0;
    while (num) {
        a[p++] = num % 2;
        num /= 2;
    }
    return dfs(p-1, true, 0, true);
}
int main() {
    int l, r;
    cin >> l >> r;
    cout << handle(r) - handle(l-1) << endl;
    return 0;
}
posted @ 2021-03-17 16:27  quanjun  阅读(83)  评论(0编辑  收藏  举报