題意:
給你三個數字L, R, K,問在[L, R]範圍內有多少個數字知足它每一位不一樣數字不超過k個,求出它們的和ios
分析:考慮用狀態壓縮 , 10給位0~9 , 若是以前出現過了某個數字x ,那就拿當前的狀態 st | (1<<x) , 表示這個數字出現了 , 那st的二進制有多少的1 , 就有多少不一樣的數 , 這裏好要考慮前導零的狀況 。c++
個數是解決了 , 可是這裏是要每一個答案的和 , 賊雞兒坑 , 通過前面的訓練能夠知道不多是在(len==0) 這裏判斷的了 , 由於是記憶化搜索 , 因此你記憶化的只是數量 ,而不是權值和 , 咱們能夠開多一個位來統計當前位的權值和 , 咱們也很容易能夠發現 ,並非單純的相加起來 , 還要相乘與符合條件 ;ide
舉例子:112和114都是知足條件的權值和 ;(112+114)=(100+10+2+100+10+4)=(2*100+2*10+2+4) ui
#include <bits/stdc++.h> using namespace std; #define ll long long 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){ 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+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; ios::sync_with_stdio(0); cin>>a>>b>>k; cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl; return 0; }