【省內訓練2018-10-26】網友數
阿新 • • 發佈:2018-11-19
【思路要點】
- 首先,當 ,任何 的數 均為網友數。
- 的證明:
當 ,我們可以暴力驗證命題的正確性。
否則,考慮 的末位,我們可以用 個 或是 造出 至 以內的任何末位,這樣,我們可以在 中將造出的數減去, 將減少一位。- 假設 ,考慮如何判斷一個數 是否為網友數。
- 考慮列舉每一位的網友數分佈,記 表示考慮了 最高的 位,餘數為 ,已用了 個網友數的狀態是否能夠達到。轉移時首先列舉下一位使用的網友數的數量 ,應當保證 ,再列舉 的數量 ,從而算出 的數量 ,新的餘數 即為 。最後判斷是否能夠達到 為位數, 的狀態即可。注意到 每過一位就會 ,因此 的狀態均為無效狀態,可以剪枝。
- 考慮原題,我們顯然需要數位 ,但是直接數位 會重複計算一個數。因此,我們不妨把前面的 作為狀態壓入最終的數位 中。即將上述的 作為一個整體計入狀態,這大約是 個 位,看似狀態量十分龐大。
- 但實際上,從初始狀態出發,經過有限步合法轉移,能夠到達的狀態是十分有限的,取 時,能夠到達的狀態僅有 種,因此,直接按照前述 即可。
- 時間複雜度 ,其中 , 表示 的位數。
- 我們甚至可以將本題改為多組詢問,可以得到 的時間複雜度。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } namespace force { const int MAXN = 38; bool valid[MAXN]; vector <int> good; ll calc(ll r) { ll ans = 0; for (int i = 1; i <= 37 && i <= r; i++) ans += valid[i]; if (r >= 37) ans += r - 37; return ans; } void work(ll l, ll r) { valid[0] = true; for (int i = 1; i <= 9; i++) { for (int j = 37; j >= 0; j--) if (valid[j]) { if (j + 4 <= 37) valid[j + 4] = true; if (j + 7 <= 37) valid[j + 7] = true; } } writeln(calc(r) - calc(l - 1)); } } ll ql, qr, k; namespace DynamicProgramming { const int states = 1654; const int MAXS = 1800; const int MAXN = 25; unordered_map <bitset <128>, int> mp; bitset <128> trans(bitset <128> now, int nxt) { bitset <128> res; res.reset(); for (int i = 0; i < 128; i++) { if (!now[i]) continue; int Residue = i / 10, Used = i % 10; for (int used = Used; used <= k; used++) for (int four = used; four >= 0; four--) { int residue = Residue * 10 + nxt - four * 4 - (used - four) * 7; if (residue < 0) break; if (residue <= 6) res.set(residue * 10 + used); } } return res; } int l, r, tot, bits, a[MAXN], edge[MAXS][10]; ll dp[MAXN][2][MAXS]; bitset <128> q[MAXS]; ll getans(int pos, bool type, int state) { if (pos == 0) { bool ans = false; for (int i = 0; i <= k; i++) ans |= q[state][i]; return ans; } if (dp[pos][type][state] != -1) return dp[pos][type][state]; ll ans = 0; for (int i = 0; i <= (type ? a[pos] : 9); i++) ans += getans(pos - 1, type && (i == a[pos]), edge[state][i]); return dp[pos][type][state] = ans; } ll work(ll r) { bits = 0; while (r != 0) { a[++bits] = r % 10; r /= 10; } memset(dp, -1, sizeof(dp)); return getans(bits, 1, 1); } void main() { l = r = tot = 1, q[1] = 1; mp[1] = tot++; while (l <= r) { bitset <128> now = q[l++]; for (int i = 0; i <= 9; i++) { bitset <128> dest = trans(now, i); if (!mp.count(dest)) { mp[dest] = tot++; q[++r] = dest; } } } //cerr << r << endl; for (int i = 1; i <= r; i++) for (int j = 0; j <= 9; j++) edge[i][j] = mp[trans(q[i], j)]; writeln(work(qr) - work(ql - 1)); } } int main() { read(ql), read(qr), read(k); if (k >= 9) force :: work(ql, qr); else DynamicProgramming :: main(); return 0; }