用了数位dp递推的方式。
思路:
表示以
开头
位的0数。
表示以
开头
位的数总数。
;
如果的话还要加上
;
这样DP数组就求出来了,接下来的问题就是怎么计算。
与一般数位DP不同的是,假如之前有位是0的话,这一位的结果还要加上
其中i小于这一位。(想一想,为什么?)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vl;
typedef vector<vl> vll;
typedef vector<double> vd;
typedef vector<vd> vdd;
typedef pair<int, int> ii;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const ll INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
ll n, m, k;
vll dp, cnt;
ll count(int dit, int first){
if(!dit)
return cnt[first][dit] = 0;
if(dit == 1)
return cnt[first][dit] = 1;
if(cnt[first][dit] >= 0)
return cnt[first][dit];
ll res = 0;
for(int i = 0; i < 10; i++)
res += count(dit - 1, i);
return cnt[first][dit] = res;
}
ll recur(int dit, int first){
if(!dit)
return dp[first][dit] = 0;
if(dit == 1)
return dp[first][dit] = !first;
if(dp[first][dit] >= 0)
return dp[first][dit];
ll res = 0;
for(int i = 0; i < 10; i++)
res += recur(dit - 1, i);
if(!first)
res += cnt[first][dit];
return dp[first][dit] = res;
}
ll cal(ll x){
if(x < 1)
return 0;
vi dit;
while(x){
dit.push_back(x % 10);
x /= 10;
}
ll res = 0;
for(int i = 1; i < (int)dit.size(); i++)
for(int j = (i == 1 ? 0 : 1); j < 10; j++)
res += dp[j][i];
ll t = 0;
for(int i = dit.size(); i > 0; i--){
ll num = 0;
for(int j = (i == (int)dit.size() && (int)dit.size() > 1); j < dit[i - 1]; j++){
res += dp[j][i];
num += cnt[j][i];
}
res += t * num;
t += !dit[i - 1];
}
return res;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cnt.resize(20, vl(20, -1));
dp.resize(20, vl(20, -1));
for(int i = 0; i < (int)dp.size(); i++)
for(int j = 0; j < 10; j++){
count(i, j);
recur(i, j);
}
while(cin >> n >> m && m >= 0)
cout << cal(m + 1) - cal(n) << endl;
cerr << "execute time: " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
未来可期。