luogu P4027 [NOI2007] 货币兑换
题面传送门
一道毒瘤题我调两天......
显然有\(dp\)式\(dp_i=\max\limits_{j=1}^{i-1}{X_j*a_i+Y_j*b_i}\)其中\(X_i=\frac{dp_ir_i}{r_ia_i+b_i},Y_i=\frac{x_i}{r_i}\)
设\(k<j\)且\(k\)劣于\(j\)那么就有式子\(-\frac{a_i}{b_i}<\frac{Y_k-Y_j}{X_k-X_j}\)
那么就可以斜率优化了。
但是这道题斜率不递增。
那么就是\(cdq\)分治走起。
总体保证顺序不变。左半边构造单调栈保证\(X_i\)递增,右半边保证\(Y_i\)递增即可。
代码实现:
#include<cstdio>
#include<algorithm>
#include<cmath>
#define eps 1e-6
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,q[100039],head,tail;
double ans,dp[100039];
struct yyy{double a,b,r;int id;double x,y,k;}s[100039],b[100039];
inline bool cmp1(yyy x,yyy y){return x.k<y.k;}
inline bool cmp3(yyy x,yyy y){return fabs(x.x-y.x)<=eps?x.y>y.y:x.x<y.x;}
inline double slope(int x,int y){return (b[y].y-b[x].y)/(b[y].x-b[x].x);}
inline void slove(int x,int y){
if(x==y) {dp[x]=max(m,dp[x]);return;}
int m=x+y>>1,i,j;
slove(x,m);
for(i=x;i<=y;i++) b[i]=s[i];
for(i=x;i<=m;i++) b[i].y=dp[b[i].id]/(b[i].a*b[i].r+b[i].b),b[i].x=b[i].y*b[i].r;
sort(b+x,b+m+1,cmp3);sort(b+m+1,b+y+1,cmp1);head=0;
for(i=x;i<=m;i++){
if(i!=x&&b[i].x-b[i-1].x<eps) continue;
while(head>=2&&slope(q[head-1],q[head])<slope(q[head],i)) head--;q[++head]=i;
}
for(i=m+1;i<=y;i++){
while(head>=2&&slope(q[head-1],q[head])<=b[i].k)head--;
j=q[head];dp[b[i].id]=max(b[j].x*b[i].a+b[j].y*b[i].b,dp[b[i].id]);
}
for(i=m+1;i<=y;i++) dp[i]=max(dp[i-1],dp[i]);
slove(m+1,y);
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d%d",&n,&m);
dp[1]=m;
for(i=1;i<=n;i++) scanf("%lf%lf%lf",&s[i].a,&s[i].b,&s[i].r),s[i].id=i,s[i].k=-s[i].a/s[i].b;
slove(1,n);
printf("%.5lf\n",dp[n]);
}