「LibreOJ β Round #2」貪心只能過樣例
阿新 • • 發佈:2020-09-02
知識點: bitset,01 揹包
原題面 Loj
題意簡述
給定 \(n\) 個數,\(x_i\) 的取值範圍 \([a_i,b_i]\)。
求不同的 \(\sum\limits_{i=1}^{n}x_{i}^{2}\) 的種類數。
\(1\le n,a_i,b_i\le 100\)。
分析題意
資料範圍比較喜人,\(\sum x_i^2\) 的值域較小,考慮一波暴力。
開 n 個 vector,記錄 \(1\sim i\) 能組成的所有數。
對於第 \(i+1\) 個數,列舉 \(1\sim i\) 能組成的所有數進行轉移,可以寫出下面的暴力程式碼。
\(n,a_i,b_i\) 同階,複雜度是 \(O(n^4)\)
發現這個轉移很像 01 揹包的轉移,加之值域較小。
考慮直接用 01 揹包求能組成哪些數。
設 \(f_{i,j} = (0/1)\) 表示當前列舉到第 \(i\) 個數,能否通過 \(1\sim i\) ,組成 \(j\)。
有狀態轉移方程:
\[f_{i,j} = [\exist x\in [a_i,b_i],[f_{i-1,j-x^2}]] \]
01 揹包倒序列舉消去第一維,複雜度 \(O(n^2)\) 級別。
發現用個桶判定出現這裡好傻逼啊,考慮用 bitset 進行維護。
轉移與上述 01 揹包的方法本質相同,直接右移或起來即可。
程式碼實現
正解
//知識點:bitset /* By:Luckyblock */ #include <algorithm> #include <cstdio> #include <ctype.h> #include <cstring> #include <bitset> #define ll long long const int kMaxn = 110; //============================================================= int n; std :: bitset <kMaxn * kMaxn * kMaxn> vis[kMaxn]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void GetMax(int &fir_, int sec_) { if (sec_ > fir_) fir_ = sec_; } void GetMin(int &fir_, int sec_) { if (sec_ < fir_) fir_ = sec_; } //============================================================= int main() { n = read(); vis[0][0] = true; for (int i = 1; i <= n; ++ i) { int a = read(), b = read(); for (int j = a; j <= b; ++ j) { vis[i] |= (vis[i - 1] << (j * j)); } } printf("%d\n", vis[n].count()); return 0; }
暴力
//傻逼暴力/cy //判定出現這裡好傻逼啊,改成bitset試試 #include <algorithm> #include <cstdio> #include <ctype.h> #include <cstring> #include <vector> #define ll long long const int kMaxn = 110; //============================================================= int n; bool vis[kMaxn][kMaxn * kMaxn * kMaxn]; std :: vector <int> ans; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void GetMax(int &fir_, int sec_) { if (sec_ > fir_) fir_ = sec_; } void GetMin(int &fir_, int sec_) { if (sec_ < fir_) fir_ = sec_; } //============================================================= int main() { n = read(); ans.push_back(0); for (int i = 1; i <= n; ++ i) { int a = read(), b = read(); std :: vector <int> tmp; for (int j = a; j <= b; ++ j) { int x = j * j; for (int k = 0, size = ans.size(); k < size; ++ k) { int now = ans[k] + x; if (! vis[i][ans[k] + x]) { vis[i][ans[k] + x] = true; tmp.push_back(ans[k] + x); } } } ans.clear(); for (int k = 0, size = tmp.size(); k < size; ++ k) { ans.push_back(tmp[k]); } } printf("%d\n", ans.size()); return 0; }