E. Segment Sum (數位dp)Educational Codeforces Round 53 (Rated for Div. 2)
阿新 • • 發佈:2018-11-05
題目連結:http://codeforces.com/contest/1073/problem/E
參考連結:https://blog.csdn.net/qq_38677814/article/details/83415782
題意:給出l,r,k,在範圍 [ l , r ] 內找出數字(滿足每個數字的數位只能有k個不同)的總和,例如:k=2,那麼101滿足只有兩種不同的數字。
題解:數位dp,詳情見程式碼,此程式碼是直接搬上面博主的,寫得太好了。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long LL; const LL MOD = 998244353LL; int cnt[20]; LL ppow[22]; LL a,b,k; struct Point{ LL x,y;///x代表符合條件的有幾個,y代表對答案的貢獻 }dp[20][1<<12][2]; Point dfs(LL len,LL state,bool limit,bool non_zero){///這裡要前導0 if(len==0) return Point{1,0};///一個數字列舉完了 符合條件的++ 不再產生貢獻(之前已經計算了) if(!limit&&dp[len][state][non_zero].y) return dp[len][state][non_zero]; ///記憶化 Point ans = Point{0,0};///初始化ans int Max = limit?cnt[len]:9;///套路 for(int i=0;i<=Max;++i){ LL temp = state|((non_zero||i)<<i); ///改變狀態 if(__builtin_popcountLL(temp)>k) continue;///刪掉錯誤的狀態 Point t = dfs(len-1,temp,limit&&i==Max,non_zero||i);///臨時變數 ans.x = (ans.x+t.x)%MOD;///符合條件的個數增加 ans.y = (ans.y+t.y+1LL*i*ppow[len-1]%MOD*t.x%MOD)%MOD;///當前數位的貢獻增加 } return dp[len][state][non_zero]=ans; } LL solve(LL x){ memset(dp,0,sizeof dp); memset(cnt,0,sizeof cnt); int len=0; while(x){ cnt[++len]=x%10; x/=10; } return dfs(len,0,true,0).y; ///最高位開始列舉 現在還沒有任何數位上有數字 到達了最高位 有前導零zero=true non_zero = false } int main(){ ppow[0]=1; for(int i=1;i<20;++i) ppow[i]=ppow[i-1]*10%MOD; while(~scanf("%lld%lld%lld",&a,&b,&k)){ printf("%lld\n",(solve(b)-solve(a-1)+mod)%mod); } return 0; }