模板 - 动态规划 - 数位dp
二进制的某道题。
int a[40];
ll dp[40][40][40];
ll dfs(int pos, int s1, int s2, bool lead, bool limit) {
if(pos == -1) {
if(s1 >= s2)
return 1;
else
return 0;
}
if(!limit && !lead && dp[pos][s1][s2] != -1)
return dp[pos][s1][s2];
int up = limit ? a[pos] : 1;
ll ans = 0;
for(int i = 0; i <= up; i++) {
int ns1 = s1 + ((!lead) && i == 0);
int ns2 = s2 + (i == 1);
ans += dfs(pos - 1, ns1, ns2, lead && i == 0, limit && i == a[pos]);
}
if(!limit && !lead)
dp[pos][s1][s2] = ans;
return ans;
}
ll solve(ll x) {
if(x < 0)
return 0;
int pos = 0;
while(x) {
a[pos++] = x % 2;
x /= 2;
}
return dfs(pos - 1, 0, 0, true, true);
}
void test_case() {
memset(dp, -1, sizeof(dp));
ll L, R;
scanf("%lld%lld", &L, &R);
printf("%lld\n", solve(R) - solve(L - 1));
}
十进制的某道题,统计[L,R]中,[0,9]的数字的出现次数,这个需要注意很多细节。比如当lead标记存在时不计算0(即使就是0也不计算)。当limit标记存在且i=d[pos]时,说明这一位取i=d[pos]之后,剩下的数字有r[pos-1]+1种,其中数组r表示原数字模10的幂次的余数(也就是十进制下的低位的数,例如:123456,当取到123???时,???的范围就是[000,456],此时对应的r值就是456,当取到122???时,解除了限制,???的范围就是[000,999])。
int k;
ll p10[17];
int d[17];
ll r[17];
ll dp[17];
ll dfs(int pos, bool lead, bool limit) {
if(pos == 0)
return 0;
if(!lead && !limit && dp[pos] != -1)
return dp[pos];
int up = limit ? d[pos] : 9;
ll ans = 0;
for(int i = 0; i <= up; i++) {
ll D = dfs(pos - 1, lead && i == 0, limit && i == d[pos]);
ans += D;
if(i == k) {
if(lead && i == 0)
continue;
if(limit && i == d[pos])
ans += r[pos - 1] + 1;
else
ans += p10[pos - 1];
}
}
if(!lead && !limit)
dp[pos] = ans;
return ans;
}
ll solve(ll x) {
if(x < 0)
return 0;
int pos = 0;
ll cx = x;
while(cx) {
d[++pos] = cx % 10;
cx /= 10;
}
for(int i = 1; i <= pos; ++i)
r[i] = x % p10[i];
return dfs(pos, true, true);
}
int cost[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
void test_case() {
p10[0] = 1;
for(int i = 1; i < 17; ++i)
p10[i] = p10[i - 1] * 10;
ll L, R;
scanf("%lld%lld", &L, &R);
ll sum = 0;
for(k = 0; k <= 9; ++k) {
memset(dp, -1, sizeof(dp));
ll cnt = solve(R) - solve(L - 1);
sum += cnt * cost[k];
}
printf("%lld\n", sum);
}