题目
计f(x)为x的数位和(如f(102)=1+0+2),t(t<=150)组样例,
每次给出n(n<=150)和k(0<=k<=9),求满足f(x)+f(x+1)+...+f(x+k)=n的最小的非负整数x
思路来源
jiangly代码
题解
①(打表假题)共k+1个数,则数位和这种东西是级别的,
设n=150,则k=0大致为1e18(9*17=153),k=1就降到1e9了,k=2就到1e6了,k=3四个数就几万了……
k=0直接贪心构造,k=1和k=2本地暴力打表,k>=3直接for现算,记忆化一下就过去了…
②(构造)由于不超过10个数,没有末一位相同的数,
枚举最后一位las,这样能算出需要进位的数的个数add,
而由于只有9进位后会变成0,在填9时只有(k+1-add)个产生了9,其余均为0,
有数位和损失,故将其与0-8分开考虑,
0-8是等价的,因为均不产生进位,k+1个8可以写作k+1个17、26,等等
不难发现,这个数最小,则从低位到高位构造时,仍需的数位和,递减的越快越好,
最后的答案的一种情况应该是,一个首位X+中间一段9+一个8+末尾一段9+枚举的最后一段las
填一些进位的9之后,剩下的位是不进位的,可以先填一个8,然后就可以填9了
而出现了这个8之后,前面的一段9就不需要进位了,有k+1个数产生贡献,更优
这题开始以为只有枚举末位和中间一段9,没考虑到989的情形,
样例44 1就说明了这一点,答案是989,而不是1889
心得
①赛中WA了之后,可以考虑用暴力n<=50和k<=9在小范围内对拍,这样容易发现问题
②发现一些小性质吧,如本题答案不超过1e18,这样就不会在赛中用string乱搞了……
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,n,k;
ll ans;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
//枚举最后一位
ans=1e18;//1e17 9*17=153
for(int las=0;las<=9;las++){
//枚举进位9的个数
//统计最后一位 中间进位9 总共的数位和
for(int nine=0;nine*9<=(n-las);++nine){
int add=0,sum=0;//进位的个数
for(int i=0;i<=k;++i){
int now=las+i;
if(now>=10)add++;
sum+=now%10;
}
sum+=nine*9*(k+1-add)+add;//进位的数的贡献 不进位的贡献
//printf("las:%d nine:%d sum:%d\n",las,nine,sum);
sum=n-sum;
if(sum<0 || sum%(k+1))continue;//高位均分k+1份
sum/=(k+1);
int mid,pre;//最高位pre
ll tmp=0;
if(sum<=8){
mid=sum;
}
else{
mid=8;
sum-=mid;
pre=sum%9;
sum-=pre;
tmp=pre;
for(int l=1;l<=sum/9;++l){//中间一段不进位的9
tmp=tmp*10+9;
}
}
tmp=tmp*10+mid;//填一个8
for(int l=1;l<=nine;++l){//后面一段进位的9
tmp=tmp*10+9;
}
tmp=tmp*10+las;//末位枚举的las
ans=min(ans,tmp);
}
}
printf("%lld\n",ans==1e18?-1:ans);
}
return 0;
}