#10470. 「2020-10-02 提高模擬賽」流水線 (line)
阿新 • • 發佈:2020-10-25
題面:#10470. 「2020-10-02 提高模擬賽」流水線 (line)
題目中的那麼多區間的條件讓人感覺極其難以維護,而且貪心的做法感覺大多都能 hack 掉,因此考慮尋找一些性質,然後再設計 DP 狀態。
設兩端區間\(Q_i\)和\(Q_j\)滿足\(Q_i \subseteq Q_j\),那麼顯然\(Q_j\)要麼單獨一組,要麼就和\(Q_i\)一組。
證明使用反證法,設\(Q_j\)與其他某些一組,那麼我把\(Q_j\)放入\(Q_i\)那一組,顯然兩組的答案都不會變少。
因此我們認為\(Q_j\)這一段無用了,當且僅當它單獨一組時我們再計算它的貢獻\(t_j-s_j\)。剩下的區間顯然滿足性質:左端點與右端點分別遞增,於是就可以 DP 了。設\(f_{i,j}\)
正確性來源於這些區間的並就等於\([s_i,t_{k+1}]\),使用單調佇列優化可以實現\(\Theta(n^2)\)。最後列舉一下我選幾個之前不要的那種大區間,從大到小列舉,就可以了。
然後就不得不提這題實現的諸多細節了,因為我們要保證答案更新時一定是從合法的值更新,所以\(f\)陣列的初值統統要設為負無窮,可以避免非常多的細節,還有f[0][0]=0那一句其實是最妙的,能夠解決很多初值的問題。
memset(f, -127 / 3, sizeof f); f[0][0] = 0; for (int i = 1; i <= k; i++) { q[head = tail = 1] = i - 1; for (int j = i; j <= cnt; j++) { while (t[nw[q[head] + 1]] <= s[nw[j]] && head <= tail) { head++; } f[j][i] = f[q[head]][i - 1] + t[nw[q[head] + 1]] - s[nw[j]]; while (head <= tail && f[j][i - 1] + t[nw[j + 1]] >= f[q[tail]][i - 1] + t[nw[q[tail] + 1]]) { tail--; } q[++tail] = j; } } LL ans = 0, now = 0; for (int i = 0; i <= k; i++) { if (f[cnt][k - i]) ans = max(ans, now + f[cnt][k - i]); if (hp.empty()) break; now += hp.top(); hp.pop(); }