题意: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;
}