Solution -「NOI 2017」「洛谷 P3826」蔬菜
阿新 • • 發佈:2022-05-24
\(\mathscr{Description}\)
Link.
原題意比較簡潔了。注意一下賣出的菜也會變質,且讓它們代替未賣出的菜變質是更優的。
\(\mathscr{Solution}\)
一眼網路流,嘗試建圖。在原題意上建圖的話我得到了一個五層結點的圖……於是可以以逆向時間描述問題。不難得到模型:
- \(S\) 連向 \((i,t)\),容量為菜 \(i\) 在 \(t\) 時刻變質的數量,費用為 \(a_i\);若 \(t\) 時刻後 \(i\) 全部變質,則分出一單位流量增加 \(s_i\) 的費用。
-
\((i,t)\) 連向 \((i,t-1)\),容量為 \(+\infty\)
- \((i,t)\) 連向 \(T_t\),容量為 \(+\infty\),費用為 \(0\);
- \(T_t\) 連向 \(T\),容量為 \(m\),費用為 \(0\)。
對於單個詢問,該圖的最大費用任意流(其實必然是最大流)費用就是答案。直接來貌似可以獲得比較可觀的分數。
接下來,我們手動分析流網路,通過比較模式化的分析找到結論。
考慮按(正向)時間順序加入 \(T_t\) 及其連邊的過程,從增廣的角度思考答案的更新:
圖中 IV 的增廣環不如僅走右側藍色路徑的反向路徑優秀;V 的增廣環顯然不是負環。因此,\(S\) 無法在殘餘網路上推流。迴歸到原問題,得到結論:當 \(t_1>t_2\)
求 \(t_\max\) 的答案?類似地分析可以發現,隨著時間(逆向)向前,每種蔬菜的選取數量都會越來越多。因而可以維護現有蔬菜的堆,以及可能因為 \(S\) 的流量進入“復活”的蔬菜集合。模擬 \(T_t\) 從大到小向 \(T\) 增廣的過程即可。
複雜度 \(\mathcal O(nm\log n)\)。
\(\mathscr{Code}\)
/*+Rainybunny+*/ #include <bits/stdc++.h> #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i) #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i) typedef long long LL; typedef std::pair<int, int> PII; #define fi first #define se second template <typename Tp> inline void chkmin(Tp& u, const Tp& v) { v < u && (u = v, 0); } template <typename Tp> inline void chkmax(Tp& u, const Tp& v) { u < v && (u = v, 0); } template <typename Tp> inline Tp imin(const Tp& u, const Tp& v) { return u < v ? u : v; } template <typename Tp> inline Tp imax(const Tp& u, const Tp& v) { return u < v ? v : u; } const int MAXN = 1e5, MAXM = 10; int n, m, k, a[MAXN + 5], s[MAXN + 5], c[MAXN + 5], x[MAXN + 5], p[MAXN + 5]; int sold[MAXN + 5], wait[MAXN + 5]; LL ans[MAXN * MAXM + 5]; std::vector<int> imp[MAXN + 5]; int main() { scanf("%d %d %d", &n, &m, &k); rep (i, 1, n) scanf("%d %d %d %d", &a[i], &s[i], &c[i], &x[i]); rep (i, 1, k) scanf("%d", &p[i]); int mxt = *std::max_element(p + 1, p + k + 1); rep (i, 1, n) { if (!x[i]) imp[mxt].push_back(i); else imp[imin(mxt, (c[i] + x[i] - 1) / x[i])].push_back(i); } std::priority_queue<PII> heap; per (i, mxt, 1) { for (int j: imp[i]) wait[++wait[0]] = j; rep (i, 1, wait[0]) { heap.emplace(sold[wait[i]] ? a[wait[i]] : a[wait[i]] + s[wait[i]], wait[i]); } wait[0] = 0; for (int rst = m; rst-- && !heap.empty();) { int u = heap.top().se; ++sold[u], heap.pop(); if (c[u] - (i - 1) * x[u] > sold[u]) heap.emplace(a[u], u); else if (x[u]) wait[++wait[0]] = u; } } rep (i, 1, n) if (sold[i]) { ans[++ans[0]] = a[i] + s[i]; while (--sold[i]) ans[++ans[0]] = a[i]; } std::sort(ans + 1, ans + ans[0] + 1); std::reverse(ans + 1, ans + ans[0] + 1); rep (i, 2, ans[0]) ans[i] += ans[i - 1]; int all = ans[0]; ans[0] = 0; rep (i, 1, k) printf("%lld\n", ans[imin(all, p[i] * m)]); return 0; }