Description
Konrad, Delfador 和 Kalenz 一行人又喜闻乐见地被追杀了。
他们面临的是一条有 N 个地点的路, 他们从 0 号地点出发, 要逃到 N 号地点去。每个地点的战斗都有一定的金币收入 Ai,也有一定的部队损失 Bi。
为了更好地逃生, Delfador 还弄到了一块传送宝石,这样一行人就能向后传送不超过 L 的距离。从一个地点传送到另一个地点时,Konrad 会选择路径上除起点外的地形指数 Ci 最大的地点进行战斗,地形指数相同时选择最靠后的。
作为优秀的领导者, Konrad 希望总金币收入与总部队损失的比值最大。
Input
第一行,两个整数 N, L。
接下来 N 行,每行两个整数,分别表示 Ai, Bi, Ci。
Output
一行,一个实数,表示答案。
答案请使用科学计数法输出,保留 9 位小数,具体参见输出样例。指数为 0 时,最后应当输出’0.000000000e+000’。
Sample Input
5 3
1 1 1
1 2 2
2 3 1
1 9 2
1 1 1
Sample Output
3.750000000e-001
Data Constraint
分析:
显然第一步就是分数规划了,转换为判定性问题,设
mid
m
i
d
为当前的答案,如果mid是合法的,有
∑a∑b>mid
∑
a
∑
b
>
m
i
d
,也就是每个点的权值变为
wi=ai−mid∗bi
w
i
=
a
i
−
m
i
d
∗
b
i
,然后判断答案是否大于等于0。
然后暴力枚举位数,也就是指数,判断一下。再从1.0~9.999二分一下,再判断。
关键就是判断了,我们设
f[i]
f
[
i
]
为到
i
i
的最大权值,显然有,
其中 i−j<=L i − j <= L , t t 为区间中 c c 值最大的位置。
显然区间中的最大值满足单调性(不严格单调减)。所以可以用单调队列维护这个下降的最大值,每次加入当前点的 c[i] c [ i ] ,就把单调队列中小于等于 c[i] c [ i ] 的全部变为 c[i] c [ i ] ,跳跃代价变为当前点代价。然后我们发现,当有两个位置的跳跃代价相同,即 c[i] c [ i ] 相同,位置靠前且 f[i] f [ i ] 更小,显然这个点一定不优。也就是相同 c[i] c [ i ] 值也要满足单调减。每次把不在范围内的头弹掉,就可以实现转移,转移的位置为 c[i] c [ i ] 值相同中的最大值,对于每个 c[i] c [ i ] 值都有可能转移,取最大值即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=1e5+7;
const double inf=1564918894981;
using namespace std;
double a[maxn],b[maxn],c[maxn],f[maxn];
double ans;
int n,L,e,h,t;
struct que{
double x,c,w,num,last,next;
}q[maxn];
bool check(double x)
{
f[0]=0;
h=1; t=1;
q[1]=(que){0,0,0,0,0,0};
for (int i=1;i<=n;i++)
{
double w=a[i]-b[i]*x;
f[i]=-inf;
while (q[h].num+L<i)
{
if (q[h].next!=h+1) q[h+1].next=q[h].next;
h++;
}
int z,g=h;
for (int j=t;j>=h;j--)
{
if (q[j].c<=c[i]) q[j].c=c[i],q[j].w=w;
else {g=j+1; break;}
}
int last=q[g].last;
q[g].last=0; q[g].next=0;
z=g;
for (int k=g+1;k<=t;k++)
{
q[k].last=0; q[k].next=0;
q[++z]=q[k];
while ((q[z].x>=q[z-1].x) && (z>g)) q[z-1]=q[z],z--;
}
t=z;
q[g].last=last;
q[last].next=g;
int j=h;
while (j>0)
{
f[i]=max(f[i],q[j].x+q[j].w);
j=q[j].next;
}
q[++t]=(que){f[i],0,0,i,t-1,0};
q[g].next=t; q[t].last=g;
}
if (f[n]>=0) return 1;
else return 0;
}
int main()
{
scanf("%d%d",&n,&L);
for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
LL k=1e8;
for (int i=1;i<=n;i++) a[i]=a[i]*k;
for (int i=-7;i<=7;i++)
{
for (int j=1;j<=n;j++)
{
if (i>0) b[j]*=10;
else a[j]/=10;
}
if (!check(10)) {e=i; break;};
}
double l=1,r=9.9999999999;
while (r-l>1e-12)
{
double mid=(l+r)/2;
if (check(mid)) l=mid,ans=mid;
else r=mid;
}
printf("%.9lf",ans);
if (e<0) printf("e-00%d",-e);
else printf("e+00%d",e);
}