Educational Codeforces Round 90 (Rated for Div. 2) E.Sum of Digits(思維題/貪心+數位和)

題目

計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個數,則數位和這種東西是\sqrt[k+1]{n}級別的,

設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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章