洛谷 P6218 [USACO06NOV] Round Numbers S

题目

数位dp , 求范围内数的二进制形式中\(cnt_0 \ge cnt_1\)的个数

思路

数位dp , 肯定要记录长度和当前位

状态转移涉及 \(0\)\(1\) 的个数 , 因此 \(f\) 中还应当有一维度为含 \(0\) 的数量

和P4999 烦人的数学作业类似 , 在 \(dp\) 过程中需要把已经经过的二进制记录 , 从而帮助判定当前的 \(0\) \(1\) 个数

dfs怎么做呢 ? 考虑记录路径上的\(lead , limit , cnt0, cnt1\)(也可以根据lead为false的位置确定总数) , 以\(cnt0 \ge cnt1\)为评判标准

代码

dp

#include<bits/stdc++.h>
using namespace std;
const int N = 33;

inline int read() {
    int ans = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * f;
}
int a[N];
int f[N][N][2];
void init() {
    f[1][1][0] = 1;
    f[1][0][1] = 1;
    for (int i =2; i<  N ; i++) {
        for (int j = 0; j<=i; j++) {
            if (j>0)
                f[i][j][0] = f[i-1][j-1][1] + f[i-1][j-1][0];
            f[i][j][1] = f[i-1][j][0] + f[i-1][j][1];
        }
    }
}
int dp(int x) {
    int cnt = 0,ans= 0;
    while (x) {
        a[++cnt] = x%2;
        x>>=1;
    }
    int s0 = 0;
    for (int i = cnt-1; i>0; i--){
        if (a[i]) {
            for (int j = 1; j<= i; j++)
                if (s0 + j >= (cnt+1)>>1)
                    ans += f[i][j][0];
        }
        s0 += (a[i]==0);
        if (s0>=cnt+1>>1 && i==1)
            ans++;
    }
    for (int i= 1; i< cnt; i++) {
        for (int j = i+1>>1; j<=i; j++) {
            ans += f[i][j][1];
        }
    }
    return ans;
}
int main() {
    init();
    int l =read(),r=read();
    cout<<dp(r)-dp(l-1);
    return 0;
}

dfs

#include<bits/stdc++.h>
using namespace std;
#define int long long int

inline int read() {
    int ans = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * f;
}
int a[40];
int f[40][40][40];
int dfs(int pos,bool limit,bool lead,int cnt0,int cnt1) {
    if (!pos) {
        return cnt0 >= cnt1;
    }
    if (!limit && ! lead && f[pos][cnt0][cnt1] >=0)
        return f[pos][cnt0][cnt1];
    int up = limit?a[pos]:1;
    int ans = 0;
    for (int i = 0; i<= up; i++) {
        ans += dfs(pos-1,limit&&i==up,lead &&i==0,cnt0+((i==0)&&!lead),cnt1+(i==1));
    }
    if (!limit && !lead) {
        f[pos][cnt0][cnt1] = ans;
    }
    return ans;
}
int work(int x) {
    int cnt = 0;
    do {
        a[++cnt] =  x % 2;
        x/=2;
    }while (x);
    return dfs(cnt,true,true,0,0);
}
signed main() {
    memset(f,-1,sizeof f);
    int l=read(),r=read();
    cout<<work(r)-work(l-1)<<"\n";
    return 0;
}
posted @ 2025-03-22 09:00  Guaninf  阅读(14)  评论(0)    收藏  举报