【題解】AHOI2009同類分布
阿新 • • 發佈:2018-05-31
efi size tin pac getc lld nbsp 一個 表示
好開心呀~果然只有不看題解做出來的題目才會真正的有一種驕傲與滿足吧ヾ(????)?"
實際上這題只要順藤摸瓜就可以了。首先按照數位dp的套路,有兩維想必是省不掉:1.當前dp到到的位數;2.0/1狀態表示是否受限制(這一條是因為有數字上限)。然後根據這兩個維度來接著往下想。第二個維度先撇開不看,我們只考慮如何從第 \(i - 1\) 位dp到第 \(i\) 位。在這裏其實卡了有點久,因為如果除數與被除數都在改變,那麽兩維的轉移是非常涼涼的。
這個時候聯想題目的特殊性質 ----- 當感覺無法優化轉移 / 轉移方式的時候,考慮狀態的重新設計 & 題目的特別要求。然後很開心的發現:\(1e18\) 實際上各位數字的和最大都只有 \(162\)。那麽豈不是亂搞也可以?所以我們固定除數 \(Q\) 為 \(\left ( 1, 162 \right )\) 當中的任意一個數,分別進行dp即可。此時的轉移就簡單了,因為除數固定,自然地追加一維表示余數。狀態固定為 \(f[i][j][k][L]\),表示dp到第 \(i\) 位,要求第 \(\left ( 1, i \right )\) 位的數字之和加起來為 \(j\),且原數除以 \(Q\) 的余數為 \(k\),限制為\(L\left ( 0, 1 \right )\)的總個數。
感覺這份代碼寫的還行,跑得也還行……能看。
#include <bits/stdc++.h> using namespace std; #define int long long int a[20], Res, mul[20]; int f[20][165][165][2]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x * k; } #define Pre f[now][tot][rm][lim] int DP(int now, int tot, int rm, bool lim) { if(~Pre) return Pre; else Pre = 0; if(tot > now * 9) return 0; if(now == 1) { if(tot > 9 || (tot > a[now] && lim)) returnPre = 0; return Pre = ((Pre = tot % Res == rm) ? 1 : 0); } for(int i = 0; i <= 9; i ++) { if(i > a[now] && lim) break; if(tot < i) break; int q = (mul[now] * i) % Res; q = q % Res; int L = (i == a[now] && lim); f[now][tot][rm][lim] += DP(now - 1, tot - i, (rm - q + Res) % Res, L); } return f[now][tot][rm][lim]; } #undef Pre int Solve(int x) { int k = x, ans = 0, num = 0; while(k) num ++, a[num] = k % 10, k /= 10; for(int i = 1; i <= 163; i ++) { Res = i; if(i > num * 9) continue; memset(f, -1, sizeof(f)); ans += DP(num, i, 0, 1); } return ans; } signed main() { int a = read(), b = read(); mul[1] = 1; for(int i = 2; i <= 20; i ++) mul[i] = mul[i - 1] * 10; printf("%lld\n", Solve(b) - Solve(a - 1)); return 0; }
【題解】AHOI2009同類分布