[SAA + SAP] 14. CloudFront
阿新 • • 發佈:2021-08-02
數位dp
一道小清新數位dp題。
乍一看,誒,這不就是個板子嘛。
但是寫著寫著就發現還是有蠻多細節的,下面我們來分析一下:
直接來看核心程式碼(即 \(dfs\) 部分)
ll dfs(ll len, ll cha, ll flag, ll lim){ if(!len) return cha >= 30; if(dp[len][cha][flag][lim] != -1) return dp[len][cha][flag][lim]; ll res = lim ? num[len] : 1; ll ans = 0; for(ll i = 0; i <= res; i++) ans += dfs(len - 1, cha + (!i ? (flag ? 0 : 1) : -1), flag && (!i), lim && (i == res)); return dp[len][cha][flag][lim] = ans; }
變數解釋:
\(len:\) 倒著查詢到了第幾位
\(cha:\) 0 比 1 多幾個,注意可能 1 比 0 多,即 \(cha < 0\),作為陣列下標的話會 \(RE\),(醬紫一點都不好哇QWQ),所以我們深搜時,令 \(cha\) 初值為 30,就解決問題了
\(flag:\) 當前位是否為前導 0 (1:是 $\ $ 0:否)
\(lim:\) 當前位是否有上界限制(1:有 $\ $ 0:無)
再來看
cha + (!i ? (flag ? 0 : 1) : -1)
如果當前位是 0,但是是前導 0,那麼 \(cha\) 不變,如果不是是前導0,那麼 \(cha++\);
如果當前位是 1,那麼 \(cha--\)
其餘的就沒什麼難點啦,不要忘記開 \(long \ long\)
上完整程式碼
#include <iostream> #include <cstdio> #include <cstring> #define ll long long using namespace std; ll l, r, len; ll num[100]; ll dp[100][100][2][2]; ll dfs(ll len, ll cha, ll flag, ll lim){ if(!len) return cha >= 30; if(dp[len][cha][flag][lim] != -1) return dp[len][cha][flag][lim]; ll res = lim ? num[len] : 1; ll ans = 0; for(ll i = 0; i <= res; i++) ans += dfs(len - 1, cha + (!i ? (flag ? 0 : 1) : -1), flag && (!i), lim && (i == res)); return dp[len][cha][flag][lim] = ans; } ll solve(ll x){ len = 0; while(x){ num[++len] = x % 2; x /= 2; } memset(dp, -1, sizeof(dp)); return dfs(len, 30, 1, 1); } signed main(){ scanf("%lld%lld", &l, &r); printf("%lld\n", solve(r) - solve(l - 1)); return 0; }
完結撒花~