第一道斜率優化題。優化
首先一個基本的狀態轉移方程是spa
要使f[i]最小,即b最小。code
對於每一個j,能夠表示爲一個點。blog
而後咱們取固定斜率時截距最小的便可,高中線性規劃。隊列
單調隊列維護下凸包。get
而後每次二分出j,轉移。編譯器
記得給(0,L * L)賦初值。io
記得開long long編譯
++,--最好別隨便用,編譯器的不一樣會讓你爆0...
1 #include <cstdio> 2 3 typedef long long LL; 4 const int N = 50010; 5 6 LL sum[N], g[N], p[N], top; 7 LL f[N], y[N]; 8 9 inline double slope(int i, int j) { 10 return ((double)(y[j] - y[i])) / (g[j] - g[i]); 11 } 12 13 inline int get(int i) { 14 if(i == 1) { 15 return 0; 16 } 17 double k = 2.0 * g[i]; 18 int l = 0, r = top, mid; 19 while(l < r) { 20 mid = (l + r) / 2; 21 //printf("%lf %lf \n", slope(p[mid], p[mid + 1]), k); 22 if(slope(p[mid], p[mid + 1]) < k) { 23 l = mid + 1; 24 } 25 else { 26 r = mid; 27 } 28 } 29 //printf("i = %d r = %d j = %d \n", i, r, p[r]); 30 return p[r]; 31 } 32 33 int main() { 34 //freopen("in.in", "r", stdin); 35 LL n, L; 36 scanf("%lld%lld", &n, &L); 37 L++; 38 for(int i = 1; i <= n; i++) { 39 LL x; 40 scanf("%lld", &x); 41 sum[i] = sum[i - 1] + x; 42 g[i] = i + sum[i]; 43 } 44 y[0] = L * L; 45 for(int i = 1; i <= n; i++) { 46 // f[i] = f[j] + (g[i] - g[j] - L) ^ 2 47 int j = get(i); 48 49 f[i] = f[j] + (g[i] - g[j] - L) * (g[i] - g[j] - L); 50 y[i] = f[i] + (g[i] + L) * (g[i] + L); 51 //printf("y[%d] = %d \n", i, y[i]); 52 53 p[++top] = i; 54 while(top > 1 && slope(p[top - 2], p[top - 1]) >= slope(p[top - 1], p[top])) { 55 p[top - 1] = p[top]; 56 top--; 57 } 58 } 59 60 /*for(int i = 1; i <= n; i++) { 61 printf("%lld ", f[i]); 62 } 63 puts("");*/ 64 printf("%lld", f[n]); 65 return 0; 66 }
[update20181208]今天又考了一次玩具裝箱,發現了一個問題.......怎麼能把點的座標直接帶入到斜截式裏面啊!!!!
只知道y - y0 = k(x - x0),歷來沒聽過y0 = kx0 + b啊啊啊!!!
關於上面那個的解釋:(感謝某蔣姓巨佬爲我講解)
上面那個式子化簡爲2gi * gj + C = F(j)
考慮有某條直線過點(gj, F(j)),且方程爲kx + b = y,其中k = 2gi
那麼將點帶入,可得:k * gj + b = F(j)
故上面那個等式即爲直線的方程。
y - F(j) = 2gi(x - gj)
y - F(j) = 2gi * x - 2gi * gj
而後反正瞎搞一搞就好了啦我也無論了啊啊啊啊阿斜率優化好難啊啊我到底在寫什麼東西啊