題目
計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;
}