1. 程式人生 > 其它 >[SAA + SAP] 14. CloudFront

[SAA + SAP] 14. CloudFront

原題連結

數位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;
}

完結撒花~