1. 程式人生 > >CodeForces1073E 數位dp+狀壓dp

CodeForces1073E 數位dp+狀壓dp

http://codeforces.com/problemset/problem/1073/E

題意 給定K,L,R,求L~R之間最多不包含超過K個數碼的數的和。

 

顯然這是一道數位dp,在做的過程中會發現為了統計數碼是否出現過這個狀態需要用到狀態壓縮

因為不同位置出現的數貢獻不同,除了傳統的dp陣列之外還需要記錄一個tot來統計這個位置之後出現數字的個數方便後面計算答案。

仔細對比了一下為什麼我要開五維陣列而網上的題解只需要開二維的陣列,發現一是因為網上的題解不對前導0和limit的情況進行記憶化搜尋,仔細想了一下這兩種情況確實分支的情況比較小,為了他開兩倍的空間的確不那麼值得。

二是因為dp除了記錄位置和是否出現過這個state之外,每個位置是什麼數事實上是不需要記錄的,對於一個位置和一個狀態事實上後續就有固定的答案,不需要額外開一個數字來記錄,對於後效答案的更新可以放在進入記憶話搜尋之前,因而數字這位數字也可以省略掉。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include 
<string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; inline int read(){int now=0;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*10+c-'0',c=getchar());return now;} #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; const double eps = 1e-9; const int maxn = 110; const int INF = 0x3f3f3f3f; const LL mod = 998244353 ; LL L,R; int K; LL ten[maxn]; int str[maxn]; LL dp[20][10][1100][2][2]; LL tot[20][10][1100][2][2]; int cnt; LL dfs(int pos,int num,int use,int k,int limit,bool zero){ if(pos == 0){ tot[pos][num][use][limit][zero] = 1; return num; } if(~dp[pos][num][use][limit][zero]) return dp[pos][num][use][limit][zero]; LL ed = limit?str[pos - 1]:9; LL sum = 0; for(int i = 0 ; i <= ed; i ++){ if(!(use & (1 << i)) && (zero || i)){ if(k + 1 > K) continue; LL x = dfs(pos - 1,i,use | (1 << i),k + 1,limit && i == str[pos - 1],zero || i); sum = (sum + x) % mod; tot[pos][num][use][limit][zero] += tot[pos - 1][i][use | (1 << i)][limit && i == (str[pos - 1])][zero || i]; }else{ LL x = dfs(pos - 1,i,use,k,limit && i == str[pos - 1],zero || i); sum = (sum + x) % mod; tot[pos][num][use][limit][zero] += tot[pos - 1][i][use][limit && i == (str[pos - 1])][zero || i]; } } tot[pos][num][use][limit][zero] %= mod; if(zero) sum = (sum + tot[pos][num][use][limit][zero] * num % mod * ten[pos]) % mod; dp[pos][num][use][limit][zero] = sum; return sum; } LL solve(LL x){ Mem(dp,-1); Mem(tot,0); if(x <= 0) return 0; cnt = 0; while(x){ str[cnt++] = x % 10; x /= 10; } return dfs(cnt,0,0,0,1,0); } int main() { ten[0] = 1; for(int i = 1; i <= 18; i ++) ten[i] = (ten[i - 1] * 10) % mod; scanf("%lld%lld%d",&L,&R,&K); LL ans1 = solve(R),ans2 = solve(L - 1); //cout << ans1 << " " << ans2 << endl; Prl((ans1 - ans2 + mod) % mod); #ifdef VSCode system("pause"); #endif return 0; }