题目链接:BZOJ 1492
其实这种用单调队列来更新答案的dp可以用平衡树这种鬼畜做法来维护,做到时间复杂度为NlogN,前不久我还写了一道用splay维护的dp题。这道题就是学习一下cdq分治,代码确实比用splay写的要短。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 100000 + 10;
int N, S, top = 0, Left = 0, Right = 0;
double eps = 1e-9;
int q[maxn];
double f[maxn];
struct node{
int id;
double a, b, rate, k, x, y;
}D[maxn], t[maxn];
inline int read(){
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
void input(){
N = read(); f[0] = read();
for(int i = 1; i <= N; ++i){
scanf("%lf%lf%lf", &D[i].a, &D[i].b, &D[i].rate);
D[i].k = - D[i].a / D[i].b; D[i].id = i;
}
}
bool cmp(node A, node B){
return A.k > B.k;
}
double get(int A, int B){
if(!A)return -1e20;
if(abs(D[A].x - D[B].x) < eps)return 1e20;
return (D[A].y - D[B].y) / (D[A].x - D[B].x);
}
void cdq(int l, int r){
if(l == r){
f[l] = max(f[l], f[l - 1]);
D[l].y = f[l] / (D[l].a * D[l].rate + D[l].b);
D[l].x = D[l].y * D[l].rate;
return ;
}
int mid = (l + r) >> 1;
int L = l, R = mid + 1;
for(int i = l; i <= r; ++i){
if(D[i].id <= mid)t[L ++] = D[i];
else t[R ++] = D[i];
}
for(int i = l; i <= r; ++i)D[i] = t[i];
cdq(l, mid);
Left = 1; Right = 0;
for(int i = l; i <= mid; ++i){
while(Right > 1 && get(q[Right], q[Right - 1]) < get(i, q[Right - 1]) + eps)Right --;
q[++Right] = i;
}
q[++Right] = 0;
for(int i = mid + 1; i <= r; ++i){
while(Left < Right && get(q[Left + 1], q[Left]) + eps > D[i].k)Left ++;
f[D[i].id] = max(f[D[i].id], D[q[Left]].x * D[i].a + D[q[Left]].y * D[i].b);
}
cdq(mid + 1, r);
L = l; R = mid + 1;
for(int i = l; i <= r; ++i){
if((D[L].x < D[R].x || (abs(D[L].x - D[R].x) < eps && D[L].y < D[R].y) || (R > r)) && L <= mid)t[i] = D[L ++];
else t[i] = D[R ++];
}
for(int i = l; i <= r; ++i)D[i] = t[i];
}
void solve(){
sort(D + 1, D + N + 1, cmp);
cdq(1, N);
printf("%.3lf\n", f[N]);
}
int main(){
input();
solve();
return 0;
}