P5785 [SDOI2012]任务安排
靠这题搞事情啊???
你会发现是经典题目斜率优化,然后我们不是单调的...
写出dp式子,转换成斜率优化形式
你会发现b是,然后k是,x是,y是
(常数我们直接吃掉)
也就是说我们每次去切的直线斜率不是单调的,但是我们横坐标还是单调递增的!
所以我们可以维护整个凸包,然后每次找最优决策点的时候二分,找到第一个直线斜率大于他的,然后前面点就是答案
即mid,mid+1斜率大于他,那么一定是和这个直线产生交点.....
然后我们上凸点还是没用的,所以仍然能维护凸包
实现细节:
- 卡精度要叉积判,nmd叉积判是要判断x差正负号的QAQAQAQ,但是x是单增的我们尽量让他是正的....
- 二分的时候我们要返回的是决策点,套que
- 维护凸包的while不要写错了
code
#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
const int MAXN = 5e5 + 7;
int t[MAXN], c[MAXN], St[MAXN], Sc[MAXN];
int que[MAXN], ex, n, l, r;
ll f[MAXN];
inline ll gx(int x) {
return Sc[x];
}
inline ll gy(int x) {
return f[x] - ex * Sc[x];
}
//常数在b里,吃掉了
//判断(x->y)<(x->y)
inline int pdslope(int x, int y, int z) {
// bool flg = 0;
// ll tmp1 = gx(x) - gx(z);
// // flg ^= (tmp1 < 0);
// ll tmp2 = gx(y) - gx(x);
// flg ^= (tmp2 < 0);
// printf("%lld %lld %lld")
// printf("slope:%lld %lld??%lld %lldby : %lld %lld %lld\n", gy(x) - gy(z), gy(y) - gy(x), tmp1, tmp2, z, x, y);
// if(flg)return (gy(y) - gy(x)) * tmp1 > (gy(z) - gy(x)) * tmp2;
return (gy(x) - gy(z)) * (gx(y) - gx(x)) >= (gy(y) - gy(x)) * (gx(x) - gx(z));
}
inline ll calc(int x, int y) {
return f[y] + St[x] * (Sc[x] - Sc[y]) + ex * (Sc[n] - Sc[y]);
}
inline int solve(ll x) {
int L = l, R = r - 1, ret = -1, mid;
// printf("!in the erfen");
while(L <= R) {
mid = (L + R) >> 1;
// printf("but :%lld %lld %lld %lld\n", L, R, gy(que[mid + 1]) - gy(que[mid]), x * (gx(que[mid + 1]) - gx(que[mid])));
if(gy(que[mid + 1]) - gy(que[mid]) > x * (gx(que[mid + 1]) - gx(que[mid]))) {
ret = mid;
R = mid - 1;//再靠后显然不行!
} else
L = mid + 1;
}
if(ret == -1)return que[r];
return que[ret];
}
signed main() {
scanf("%lld", &n);
scanf("%lld", &ex);
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld", &t[i], &c[i]);
St[i] = St[i - 1] + t[i];
Sc[i] = Sc[i - 1] + c[i];
}
memset(f, 0x3f3f3f3f, sizeof(f));
l = 0, r = 0;
f[0] = que[0] = 0;
for(int i = 1; i <= n; ++i) {
int j = solve((long double)St[i]);
f[i] = calc(i, j);
// printf("we are %lld by %lld getvalue:%lld\n", i, bst, f[i]);
// printf("in ?%lld?%lld %lld %lld\n", l, r, que[l], que[r]);
while(l < r && pdslope(que[r], i, que[r - 1])) {//前小于后
--r;
}
que[++r] = i;
}
printf("%lld\n", f[n]);
return 0;
}