先贴一个题目链接F. Interesting Function


题意:

一直,问你在这一过程中数位变化的多少。

例如 数位变化了3位。


运用我们的小学知识,我们可以知道每次至少会有个位会变化,而假如有后缀的9,因为会有进位,那么改变的数位会变多,比如

根据进位的性质,可以看出对于每个数字而言,其所引发变化的数位个数为后缀9的个数+1。

所以这个问题就变成了求区间中 每个数字的后缀9的个数+1 的和。

这个问题可以转化为求区间中 每个数字的后缀9的个数+1 的和 减去 区间中 每个数字的后缀9的个数+1 的和。


接下来可以引出两种解法,本质是相同的:

一:

假设我们并不知道数位DP。

如何求解 区间中 每个数字的后缀9的个数+1 的和呢?

后面0个9的贡献:1189

后面1个9的贡献:因为该数字为1189,所以有119个。(0009,0019,…,1179,1189)

后面2个9的贡献:11个。(0099,0199,…,0999,1099)

以此类推,其实规律就变得很明显了。 代码大体这样:

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
void solve()
{
    ll l, r;
    cin >> l >> r;
    ll asw = r - l;//对于0个9的贡献的简便处理
    r = r - 1;
    bool flag1 = 1;
    while(r)
    {
        if (r % 10 != 9)
        {
            flag1 = 0;
        }
        r /= 10;
        if (flag1)
        {
            asw += r + 1;
        }
        else
        {
            asw += r;
        }
    }
    l = l - 1;
    bool flag2 = 1;
    while(l)
    {
        if (l % 10 != 9)
        {
            flag2 = 0;
        }
        l /= 10;
        if (flag2)
        {
            asw -= l + 1;
        }
        else
        {
            asw -= l;
        }
    }
    cout << asw << "\n";
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}

二:

假如你知道了数位DP:

根据以上规律,可以总结出这样的DP状态:

表示已经填写了的数字,表示此时的后缀的个数

根据大佬的思路可以写出如下的记忆化搜索版本的数位DP:

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
const int maxn = 9 + 5;
 
int a[maxn];
ll dp[maxn][maxn];
 
ll dfs(int pos, bool limit, int cnt)
{
    if (pos == 0) return cnt + 1;
    if (!limit && dp[pos][cnt]!=-1) return dp[pos][cnt];
    ll res = 0;
    int up = limit ? a[pos] : 9;
    for (int i = 0; i <= up; i++)
    {
        res = res + dfs(pos - 1, limit && i == up, i == 9 ? cnt + 1 : 0);
    }
    if(!limit) dp[pos][cnt] = res;
    return res;
}
 
void solve()
{
    ll l, r;
    cin >> l >> r;
    ll asw = 0;
    int len = 0;
    r--;
    while (r)
    {
        a[++len] = r % 10;
        r /= 10;
    }
    asw += dfs(len,true,0);
    len = 0;
    l--;
    while (l)
    {
        a[++len] = l % 10;
        l /= 10;
    }
    asw -= dfs(len,true,0);
    cout << asw << "\n";
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    fill(*dp,*(dp+maxn),-1);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}

两种写法都是58行,很凑巧。