題解 CF1574D The Strongest Build
阿新 • • 發佈:2021-09-22
\(2000\) 分的題我賽場上居然沒有做出 qwq
想得太複雜了。
\(n\) 列數每列取一個,\(m\) 種取法不行,求和最大的取法。
受到以前貪心講課的一道題的影響,看到我就想著把前 \((m+1)\) 大的方案都求出來,存進 \((m+1)\) 個 twt
裡面,然後排個序看看哪個沒出現就是了。
如何求前 m+1 大?
考慮兩個的合併,若 \((i, j)\) 是當前最大的,那麼下一個大的可能是 \((i+1, j)\) 或 \((i, j+1)\),這兩個塞進優先佇列裡就好了,把這個過程重複 \((m-1)\) 次就可以得到前 \(m\) 大的。
程式碼
#include <iostream> #include <set> #include <vector> #include <iterator> #include <algorithm> #include <functional> #include <map> #define int long long const int base = 2333, P = 10'0000'0103; int n, k, m; std::vector<int> a[15]; struct dwd { int a, x, y; dwd(int _a, int _x, int _y) { a = _a, x = _x, y = _y; } bool operator < (dwd b) const { if (a != b.a) return a > b.a ; if (x != b.x) return x < b.x; if (y != b.y) return y < b.y; return false; } }; struct twt { std::vector<int> b; int val, hash; void push(int x) { int now = b.size(); b.push_back(x), val += a[now+1][x]; hash = (hash * base % P + a[now+1][x]) % P; } twt(int x) { hash = 0, val = 0; b.clear(); push(x); } twt() { hash = 0, b.clear(); val = 0; } bool operator < (const twt &z) const { if (val != z.val) return val > z.val; else return hash < z.hash; } bool operator != (const twt &z) { for (int i = 0; i < n; i++) if (b[i] != z.b[i]) return true; return false; } friend std::ostream& operator << (std::ostream& out, twt x) { for (int i = 0; i < (int)x.b.size(); i++) out << a[i+1][0]-x.b[i]+1 << ' '; return out; } }; std::vector<twt> s, tmp, fo; std::set<dwd> t; std::map<twt, bool> map; void merge(int q) { t.clear(), tmp.clear(); t.insert(dwd(s[0].val + a[q][1], 0, 1)); while (tmp.size() <= m+1 && !t.empty()) { int i = t.begin()->x, j = t.begin()->y; t.erase(t.begin()); twt cur = s[i]; cur.push(j); tmp.push_back(cur); if (i+1 < (int)s.size()) t.insert(dwd(s[i+1].val + a[q][j], i+1, j)); if (j+1 <= a[q][0]) t.insert(dwd(s[i].val + a[q][j+1], i, j+1)); // cnt ++; } s = tmp; std::sort(s.begin(), s.end()); } signed main() { std::cin >> n; for (int i = 1; i <= n; i++) { std::cin >> k; a[i].resize(k+1), a[i][0] = k; for (int j = 1; j <= k; j++) std::cin >> a[i][j]; std::sort(std::next(a[i].begin()), a[i].end(), std::greater<int>()); } std::cin >> m; for (int i = 1; i <= a[1][0]; i++) s.push_back(twt(i)); std::sort(s.begin(), s.end()); for (int i = 2; i <= n; i++) merge(i); for (int i = 1; i <= m; i++) { twt cur; for (int j = 1; j <= n; j++) std::cin >> k, cur.push(a[j][0]-k+1); map[cur] = 1; } int j = 0; for (auto cur : s) if (!map[cur]) return std::cout << cur, 0; }
賽時因為 比較沒有嚴格弱序 + 奇怪的錯誤 沒能通過。
其實還是因為思維不夠,馬量來湊。
稍微觀察就可以發現答案要麼是每組取最大,要麼是某個被禁掉的某一位少一個,那麼列舉就好了。
就 是 這 麼 簡 單
#include <iostream> #include <vector> #include <map> #define int long long int n, m, mx; std::vector<int> a[15], ab[100005], v, ans; std::map<std::vector<int>, bool> map; std::ostream& operator << (std::ostream& out, std::vector<int> a) { for (int i = 0; i < (int)a.size(); i++) out << a[i] << ' '; return out; } signed main() { std::ios::sync_with_stdio(false), std::cin.tie(nullptr); std::cin >> n; for (int i = 1, k; i <= n; i++) { std::cin >> k; a[i].resize(k+1), a[i][0] = k, v.push_back(a[i][0]); for (int j = 1; j <= a[i][0]; j++) std::cin >> a[i][j]; } ans = v; std::cin >> m; for (int i = 1, k; i <= m; i++) { v.clear(); for (int j = 1; j <= n; j++) std::cin >> k, v.push_back(k); map[v] = 1, ab[i] = v; } if (!map.count(ans)) return std::cout << ans, 0; for (int i = 1; i <= m; i++) { for (int j = 0; j < n; j++) { if (ab[i][j] <= 1) continue; ab[i][j] --; int an = 0; for (int k = 1; k <= n; k++) an += a[k][ab[i][k-1]]; if (!map.count(ab[i]) && an > mx) mx = an, ans = ab[i]; ab[i][j] ++; } } std::cout << ans; }
所以比賽時彆著急寫,這種“老題翻新”系列自有其自己的做法,怎麼可能讓你把原來的做法改一改就過呢?