题解 P6218 【[USACO06NOV] Round Numbers S】

· · 题解

借此题,总结数位 dp 模板。

在洛谷上打上 数位dp 的标签,这是两道蓝题之一,很适合小结。借助代码来理解吧。下面是所有核心代码。

int dfs(bool limit, bool lead, int pos, int cha) {
    if(pos==0) return cha>=30;
    if(!limit&&!lead&&vis[pos][cha]) return f[pos][cha];
    int res=0;
    int up=limit?a[pos]:1;
    for(int i=0;i<=up;i++) {
        res+=dfs(limit&(i==a[pos]), lead&(i==0), pos-1, cha+(i==0?(lead?0:1):-1));
    }
    if(!limit&&!lead) vis[pos][cha]=1, f[pos][cha]=res;
    return res;
}

变量说明:

$lead$:当前位是否是前导 0; $pos$:当前是从前向后的第 $pos$ 位。 $cha$:0 比 1 多多少。由于可能出现负数,统统加上了 30; $vis(pos,cha),f(pos,cha)$:两个记忆化数组,记录没有限制且没有前导 0 时的情况是否搜索过,并记录搜索结果。 --- 逐行解释: `if(pos==0) return cha>=30;` 如果当前所有位搜索完毕,0 比 1 多(差值大于 0)返回 1,否则返回 0。 `if(!limit&&!lead&&vis[pos][cha]) return f[pos][cha];` 如果当前没有特殊限制并且已经搜索过了,直接返回。 `int up=limit?a[pos]:1;` 当前位能去到的最大值。有限制那么就是原数中的 $pos$ 位,没有限制也只能取到 1. `res+=dfs(limit&(i==a[pos]), lead&(i==0), pos-1, cha+(i==0?(lead?0:1):-1));` 如果之前都有限制且这一位也顶上了位,那么 $limit$ 也是 1;如果之前都是前导 0 且这一位还是 0,那么这一位还是前导 0;位置就往下一位搜;计算新差值(前导 0 统统不算)。 `if(!limit&&!lead) vis[pos][cha]=1, f[pos][cha]=res;` 当前不是特殊情况,那么就记忆下来。 --- 小说明: 复杂度如何保证?当 $limit$ 是 1 或者 $lead$ 是 1 的时候并没有记忆化,那么如何保证复杂度?为了让 $limit$ 是 1,每一位都得顶位,只有 $len$ 种情况;如果让 $lead$ 是 1,那么每一位都得是 1,也只有 $len$ 种情况。因此,只有极少数情况未被记忆化,复杂度保障在 $len^2$ 的水平。 --- 提供代码,仅供参考: ```cpp #include<bits/stdc++.h> using namespace std; #define ll long long int read() { int x=0, f=1; char ch=' '; while(!isdigit(ch)) {ch=getchar(); if(ch=='-') f=-1;} while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48), ch=getchar(); return x*f; } int a[50], len, f[50][65], vis[50][65]; int dfs(bool limit, bool lead, int pos, int cha) { if(pos==0) return cha>=30; if(!limit&&!lead&&vis[pos][cha]) return f[pos][cha]; int res=0; int up=limit?a[pos]:1; for(int i=0;i<=up;i++) { res+=dfs(limit&(i==a[pos]), lead&(i==0), pos-1, cha+(i==0?(lead?0:1):-1)); } if(!limit&&!lead) vis[pos][cha]=1, f[pos][cha]=res; return res; } int solve(int x) { len=0; while(x) { a[++len]=x%2; x/=2; } return dfs(1, 1, len, 30); } int main() { int l=read(), r=read(); cout<<solve(r)-solve(l-1); } ```