Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum
阿新 • • 發佈:2018-11-02
https://codeforces.com/contest/1073/problem/E
題意
求出l到r之間的符合要求的數之和,結果取模998244353
要求:組成數的數位所用的數字種類不超過k種
思路
這題一看就是個數位dp的模板題,但是由於以前沒有完全理解數位dp加上xjb套模版,導致樣例都沒算出來
一開始這樣定義狀態
dp[i][j] 代表第cnt~i(高位到低位)位,cnt~i狀態為j的總和
一直記錄當前數字的數值,到邊界的時候返回加到答案中,但是由於這樣定義狀態會導致一個狀態對應多個數,但是他只能累加字典序最小的數到答案中,所以會使得有的數並沒有被計算
仔細分析一波,迴歸dp本身,應該找到一個子狀態,使得後面的狀態可以通過前面的狀態計算得到(轉移),其實只需要改一下定義
dp[i][j] 代表0~i(低位到高位),cnt~i+1狀態為j的總和sum和數字個數num(dp兩個東西)
在第i步選擇第i位的數字- 其實就是轉換一下計算方法,比如0~9可以加在數字1後,組成10~19,轉化為公式\(10^{i-1}*dp[i-1][j|(1<<digit)].num*digit\)
轉移為:\(dp[i][j]=\sum_{digit=0}^{9}(dp[i][j].sum+10^{i-1}*dp[i-1][j|(1<<digit)].num*digit)\)
關於數位dp
- 對於每個數求出的是小於這個數的所有滿足條件的數(字首和)
- 將一個數當成字串,然後從高位往低位看,lead看前導0是否對計數產生影響
通常定義狀態可以理解為
dp[i][j] 代表0~i(低位到高位),cnt~i+1狀態為j的計數、
- 關於lead和limit,可以放在陣列下標,也可以在函式裡特判
附上一個lead和limit在函式中特判的板子
#include<bits/stdc++.h> #define ll long long #define P 998244353 #define M 2005 using namespace std; struct N{ ll x,y; }dp[25][M]; ll pw[25],l,r; int k,a[25],i,cnt; N dfs(int p,int st,int lead,int limit){ if(!p) return __builtin_popcount(st)<=k&&!lead?N{1,0}:N{0,0}; if(!lead&&!limit&&dp[p][st].y!=-1)return dp[p][st]; N ans=N{0,0}; int end=(limit?a[p]:9); for(int i=0;i<=end;i++){ N tp=dfs(p-1,lead&&!i?0:st|(1<<i),lead&&!i,limit&&i==end); ans.x=(ans.x+tp.x)%P; ans.y=(ans.y+tp.y+1ll*i*pw[p-1]%P*tp.x%P)%P; } if(!limit)dp[p][st]=ans; return ans; } ll cal(ll x){ cnt=0; while(x>0){a[++cnt]=x%10;x/=10;} memset(dp,-1,sizeof(dp)); return dfs(cnt,0,1,1).y%P; } int main(){ pw[0]=1; for(i=1;i<=20;i++)pw[i]=pw[i-1]*10%P; cin>>l>>r>>k; cout<<(cal(r)-cal(l-1)+P)%P; }