【洛谷 P2900】 [USACO08MAR]土地征用Land Acquisition(斜率優化,單調棧)
阿新 • • 發佈:2019-01-28
答案 print ++ name printf tdi max tail ++i
題目鏈接
雙倍經驗
設\(H\)表示長,\(W\)表示寬。
若\(H_i<H_j\)且\(W_i<W_j\),顯然\(i\)對答案沒有貢獻。
於是把所有點按\(H\)排序,然後依次加入一個按\(W\)降序排序的單調棧。
這個單調棧裏就是一定對答案有貢獻的點,現在的問題就是把這些點分段,使總費用最小。
設\(f[i]\)表示前\(i\)塊土地的最小費用。
然後枚舉斷點\(0<=j<i\),則\(f[i]=\min(f[j]+W_{j+1}*H_i)\)
斜率優化搞一搞就行了。
// f[i] = f[j] + x[i] * y[j + 1] // f[j] = -x[i] * y[j + 1] - f[i]
#include <cstdio> #include <algorithm> using namespace std; const int MAXN = 50010; #define ll long long inline ll min(const ll a, const ll b){ return a < b ? a : b; } int n, top; struct point{ int x, y; int operator < (const point A) const{ return x != A.x ? x < A.x : y < A.y; } }a[MAXN], st[MAXN]; int q[MAXN], head, tail; ll f[MAXN]; inline double k(int i, int j){ return (f[i] - f[j]) / (st[i + 1].y - st[j + 1].y); } int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d", &a[i].x, &a[i].y); sort(a + 1, a + n + 1); for(int i = 1; i <= n; ++i){ while(top && st[top].y <= a[i].y) --top; st[++top] = a[i]; } for(int i = 1; i <= top; ++i){ while(head < tail && k(q[head], q[head + 1]) > -st[i].x) ++head; int j = q[head]; f[i] = f[j] + (ll)st[i].x * st[j + 1].y; while(head < tail && k(q[tail - 1], q[tail]) <= k(q[tail], i)) --tail; q[++tail] = i; } printf("%lld\n", f[top]); return 0; }
【洛谷 P2900】 [USACO08MAR]土地征用Land Acquisition(斜率優化,單調棧)