题解
将费用提前计算可以得到状态转移方程: $F_i = min(F_j + sumT_i * (sumC_i - sumC_j) + S imes (sumC_N - sumC_j)$
把方程进行分离, 得到 $S imes sumC_j + F_j = sumT_i imes sumC_j + F_i - S imes sumC_N$。
将等号左边看成纵坐标, $sumC_j$看成横坐标, $sumT_i$为斜率来进行斜率优化。
由于 $sumT_i$是递增的, 即斜率是递增的, 维护一个单调队列, 第一个斜率大于$sumT_i$的端点就为决策点
斜率优化dp还是很套路的
代码
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rd read() 5 #define rep(i,a,b) for( int i = (a); i <= (b); ++i ) 6 #define per(i,a,b) for( int i = (a); i >= (b); --i ) 7 using namespace std; 8 9 const int N = 1e4 + 1e3; 10 11 int n, m, sumT[N], sumC[N], S, f[N], q[N]; 12 13 int cross(int a, int b, int c) { //点积 14 int ax = sumC[a], bx = sumC[b], cx = sumC[c]; 15 int ay = f[a], by = f[b], cy = f[c]; 16 int x = bx - ax, xx = cx - ax, y = by - ay, yy = cy - ay; 17 return x * yy - xx * y; 18 }// 向量ab, ac 19 20 double calk(int a, int b) { 21 int ax = sumC[a], bx = sumC[b], ay = f[a], by = f[b]; 22 return 1.0 * (by - ay) / (bx - ax); 23 } 24 25 int read() { 26 int X = 0, p = 1;char c = getchar(); 27 for(; c > '9' || c < '0'; c = getchar() ) if( c == '-' ) p = -1; 28 for(; c >= '0' && c <= '9'; c = getchar() ) X = X * 10 + c - '0'; 29 return X * p; 30 } 31 32 int main() 33 { 34 n = rd; S = rd; 35 rep(i, 1, n) { 36 int t = rd, c = rd; 37 sumT[i] = sumT[i - 1] + t; 38 sumC[i] = sumC[i - 1] + c; 39 } 40 memset(f, 0x3f, sizeof(f)); 41 int l = 1, r = 1; 42 q[1] = f[0] = 0; 43 rep(i, 1, n) { 44 while(l < r && calk(q[l], q[l + 1]) <= S + sumT[i]) l++; 45 if(l <= r) f[i] = f[q[l]] + sumT[i] * (sumC[i] - sumC[q[l]]) + S * (sumC[n] - sumC[q[l]]); 46 while(l < r && cross(q[r - 1], q[r], i) <= 0) r--; 47 q[++r] = i; 48 } 49 printf("%d ",f[n]); 50 }