2019 Multi-University Training Contest 8 1003 Acesrc and Good Numbers ——数位DP求n使得1-n中d的数量=n

本文探讨了一个数学问题,即找到最大的x<=n,使得f(d,x)=x,其中f(d,x)表示1-x中d的出现次数。通过分析函数特性,采用记忆化搜索优化算法,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

This way

题意:

f ( d , x ) f(d,x) f(d,x)表示1-x中d的出现次数。现在给你d,n,让你求最大的x<=n使得 f ( d , x ) = x f(d,x)=x f(d,x)=x

题解:

它可以用两个函数表示:y=x和一个波动函数的交点即是 f ( d , x ) = x f(d,x)=x f(d,x)=x的解。那么如果 f ( d , n ) = n f(d,n)=n f(d,n)=n,答案就是n,否则将n-max(1,abs(f(d,n)-n)/17),因为n-1比n最多小18位,然后我们每次至少减1位,如此往复即可得到正解,而n与f(d,n)是同一个数量级的,那么其实要做的次数并不多。
记忆化搜索需要优化,对于当前位置,所有小于等于当前位置值得情况,都有 ( p o s − 1 ) ∗ 1 0 p o s − 2 (pos-1)*10^{pos-2} (pos1)10pos2种情况,这个手模一下即可得到:
1-9有1种可能,1-99有210种可能,1-999有3100种可能。。。
那么对于当前位置是d的情况,分成两种:如果它不是上界,那么有 1 0 p o s − 1 10^{pos-1} 10pos1种可能,否则就是剩下的数种可能,比如d=1
那么2000的时候1在第4位有1000种可能,但是1005时,只有6种可能。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll p[20],nex[20],a[20];
int d;
ll dfs(int pos)
{
    if(pos<=0)
        return 0;
    ll ans=(ll)(a[pos])*(pos-1)*(pos-2>=0?p[pos-2]:0ll)+(d<a[pos])*p[pos-1];
    a[pos]==d&&(ans+=nex[pos-1]+1);
    ans+=dfs(pos-1);
    return ans;
}
int main()
{
    p[0]=1;
    for(int i=1;i<=18;i++)
        p[i]=p[i-1]*10ll;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll x;
        scanf("%d%lld",&d,&x);
        ll ret=x;
        for(int i=1;i<20;i++)
            a[i]=ret%10,nex[i]=nex[i-1]+a[i]*p[i-1],ret/=10;
        ll y=dfs(19);
        while(x!=y)
        {
            x-=max((x-y>=0?x-y:y-x)/18,1ll);
            ret=x;
            for(int i=1;i<20;i++)
                a[i]=ret%10,nex[i]=nex[i-1]+a[i]*p[i-1],ret/=10;
            y=dfs(19);
        }
        printf("%lld\n",x);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值