E. Segment Sum
阿新 • • 發佈:2019-01-13
感覺是一道比較難的數位dp
數位dp的做法顯然 單點在於那麼多狀態該怎麼記錄 我寫了好久沒寫出來 看了別人的程式碼發現很妙
首先預處理出 10的冪次 這個不過多講
cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl;
這個是數位dp的套路
#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+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;
ios::sync_with_stdio(0);
cin>>a>>b>>k;
cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl;
return 0;
}