[數位DP][AHOI2009] Luogu P4127 同類分佈
阿新 • • 發佈:2020-07-10
最後開long long過了, 心累, 摸了, 明天再寫
# include <iostream> # include <cstdio> # include <cstring> # define LL long long # define MAXN 22 using namespace std; int sum, a[MAXN]; // sum 記錄各個位數的和 // a[0]記錄當前數的總位數, 後面依次記錄位 LL f[MAXN][MAXN*9][MAXN*9]; // f[dep][cur][mod] -> 當前 dp 到了第 dep 位, 現在該數的各位之和為 cur, mod 為 % 當前情況的sum值 LL DFS(int dep, int cur, int mod, bool eq){ if(cur > sum) return 0; // 當總和比當前統計的情況的和還大說明不符合當前的情況 if(!dep) return mod == 0 && cur == sum; // 如果當前各位數之和與要求的各位數之和相等, 且 mod=0, 就是一個符合條件的答案 if((!eq)&&(~f[dep][cur][mod])) return f[dep][cur][mod]; // 如果當前不是恰好是答案的詢問就可以套記憶化搜尋 // 注意記憶化搜尋得到的值都是按照每位的範圍為 0~9 計算的, 拿來更新 eq 為真的值就 boom 了 // 若當前的值沒有超出邊界而且第一次被搜尋到 int lim = (eq? a[dep] : 9); // 確定下一位可以選擇的最大值 LL ans = 0; for(int i = 0; i <= lim; i++) // 搜尋下一位的數位情況, 注意下一位數位從 0 開始 ans += DFS(dep-1, cur+i, (mod*10+i)%sum, eq&&(i==lim)); if(!eq) // 當前的答案可以用於更新記憶化搜尋的值 f[dep][cur][mod] = ans; return ans; } // dep -> 當前所在的位數 // cur -> 當前的數位之和 // mod -> 當前的數 %sum 的值 // eq -> 記錄上一位填入的數是否和 a[] 中的數相等 LL DP(LL x){ a[0] = 0; for(LL i = x; i; i/=10) a[++a[0]] = i%10; // 預處理 a[i] 陣列 LL ans = 0; for(int i = 1; i <= a[0]*9; i++){ // 各個位數之和一定不超過 位數*9 sum = i; // 統計位數和為 i 的情況 memset(f, -1, sizeof(f)); // 初始化 f 為全 1 ans += DFS(a[0], 0, 0, 1); // DFS 過程 } return ans; } int main(){ LL l, r; cin>>l>>r; cout<<DP(r) - DP(l-1); return 0; }