J - Deciphering Oracles Gym - 101492J

题意:1~n之间的数字重新排序,排序规则是:如果x<y (x的位数求和小于y的位数求和,或者x的位数求和等于y的位数求和且x<y)。

题解:数位dp求n的位数和小于sum的个数,对于第一个答案,我们首先求出k的位数和ans,然后求出k在ans的是第几位,然后求出n在ans-1是的个数,最后两个相加就求出来了。 第二个答案,首先for循环求出第k位在那个位数和内,在这个位数和的第几个,然后二分求出最小的满足条件的u。

 

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int data[20];
ll dp[25][300];

ll dfs(int pos,int ai,int sum,bool limit)
{
    if(pos==-1) return ai<=sum;
    if(!limit&&dp[pos][ai]!=-1) return dp[pos][ai];
    int up= limit ? data[pos] : 9;
    ll ans=0;
    for(int i=0;i<=up;i++)
    {
        ans+=dfs(pos-1,ai+i,sum,limit&&i==data[pos]);
    }
    if(!limit) dp[pos][ai]=ans;
    return ans;
}

ll solve(ll num,int sum) //求出1~num之间位数和为sum的个数。
{
    memset(dp,-1,sizeof(dp));
    int pos=0;
    while(num)
    {
        data[pos++]=num%10;
        num/=10;
    }
    return dfs(pos-1,0,sum,true);
}
ll n,k;

void work(){ //求出k位是谁
    ll flag,v;
    for(int i=1;i<=180;i++){ //求出k在那个sum里,然后标记。
        ll p=solve(n,i); ll u=solve(n,i-1);
        if(p-1>=k){
            flag=i; v=k-u+1;
            break;
        }
    }
    ll l=1,r=n,u;
    while(l<=r){ //二分答案求出结果
        ll mid=(l+r)>>1;
        ll a=solve(mid,flag),b=solve(mid,flag-1);
        if(a-b>=v) r=mid-1,u=mid;
        else l=mid+1;
    }
    cout<<' '<<u<<endl;
}

void work2(){   //求出k在第几位

    ll x=k,ans=0;
    while(x){ ans+=x%10; x/=10; }
    ll p=solve(n,ans-1)-1+solve(k,ans)-solve(k,ans-1);
    cout<<p;
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>k;
    work2();
    work();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值