jzoj4017 [雅礼联考DAY01]逃跑 二分+dp+线段树

Description


Konrad, Delfador 和 Kalenz 一行人又喜闻乐见地被追杀了。
他们面临的是一条有 N 个地点的路, 他们从 0 号地点出发, 要逃到 N 号地点去。每个地点的战斗都有一定的金币收入 Ai,也有一定的部队损失 Bi。
为了更好地逃生, Delfador 还弄到了一块传送宝石,这样一行人就能向后传送不超过 L 的距离。从一个地点传送到另一个地点时,Konrad 会选择路径上除起点外的地形指数 Ci 最大的地点进行战斗,地形指数相同时选择最靠后的。
作为优秀的领导者, Konrad 希望总金币收入与总部队损失的比值最大。

答案请使用科学计数法输出,保留 9 位小数,具体参见输出样例。指数为 0 时,最后应当输出’0.000000000e+000’。

Solution


注意到这是一个分数规划的形式,考虑二分答案然后dp求最值
不难得到dp转移方程:f[i]=max(f[j]+w[k]),其中i-j<=L,k为连续一段中的最大值所处位置
然后考试的时候并没有想着怎么优化,打了个n^2dp然后rmq求最值,结果输出大于10的科学计数法挂了gg

一个比较显然的想法就是维护单调递减的队列,那么队列中的每一个元素都会影响一整段区间的f(考虑数组和转移的定义
很快我们就能发现对于单调队列中的删除、插入操作也只会影响最右一段区间的f,那么对于这种dp我们用一棵线段树记录区间最大f和区间最大f+对应的w即可,区间修改可以不用下传直接求和

Code


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)

typedef std:: pair <double,double> pair;
const int INF=1e9;
const int N=60005;
const double eps=1e-7;

struct treeNode {double max,tag;} t[N*4];

int a[N],b[N],c[N],n,m;
int queue[N];

double w[N],f[N],g[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void modify(int now,int tl,int tr,int l,int r,double v) {
    if (tl==l&&tr==r) {
        t[now].max+=v; t[now].tag+=v;
        return ;
    }
    int mid=(tl+tr)>>1;
    if (r<=mid) modify(now<<1,tl,mid,l,r,v);
    else if (l>mid) modify(now<<1|1,mid+1,tr,l,r,v);
    else {
        modify(now<<1,tl,mid,l,mid,v);
        modify(now<<1|1,mid+1,tr,mid+1,r,v);
    }
    t[now].max=std:: max(t[now<<1].max,t[now<<1|1].max)+t[now].tag;
}

void change(int now,int tl,int tr,int x,double v) {
    if (tl==tr) {
        t[now].max=v;
        return ;
    }
    int mid=(tl+tr)>>1;
    if (x<=mid) change(now<<1,tl,mid,x,v);
    else if (x>mid) change(now<<1|1,mid+1,tr,x,v);
    t[now].max=std:: max(t[now<<1].max,t[now<<1|1].max)+t[now].tag;
}

double query(int now,int tl,int tr,int l,int r) {
    if (tl==l&&tr==r) return t[now].max;
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(now<<1,tl,mid,l,r)+t[now].tag;
    if (l>mid) return query(now<<1|1,mid+1,tr,l,r)+t[now].tag;
    double qx=query(now<<1,tl,mid,l,mid);
    double qy=query(now<<1|1,mid+1,tr,mid+1,r);
    return std:: max(qx,qy)+t[now].tag;
}

void build(int now,int tl,int tr) {
    t[now]=(treeNode) {0,0};
    if (tl==tr) {
        t[now].max=f[tl];
        return ;
    }
    int mid=(tl+tr)>>1;
    build(now<<1,tl,mid);
    build(now<<1|1,mid+1,tr);
    t[now].max=std:: max(t[now<<1].max,t[now<<1|1].max);
}

bool check(double mid) {
    rep(i,1,n) f[i]=-INF; f[1]=0;
    build(1,1,n);
    rep(i,1,n) w[i]=a[i]-mid*b[i];
    int head=1,tail=0; queue[++tail]=1;
    rep(i,2,n) {
        while (head<tail&&c[i]>=c[queue[tail]]) {
            modify(1,1,n,queue[tail-1],queue[tail]-1,-w[queue[tail]]);
            tail--;
        }
        queue[++tail]=i;
        modify(1,1,n,queue[tail-1],queue[tail]-1,w[queue[tail]]);
        f[i]=query(1,1,n,std:: max(1,i-m),i-1);
        change(1,1,n,i,f[i]);
    }
    return f[n]<=0;
}

int main(void) {
    // freopen("data.in","r",stdin);
    // freopen("myp.out","w",stdout);
    n=read()+1,m=read();
    rep(i,2,n) a[i]=read(),b[i]=read(),c[i]=read();
    double l=0,r=1000000;
    rep(i,1,60) {
        double mid=(l+r)*0.5;
        if (check(mid)) r=mid;
        else l=mid;
    }
    double prt=(l+r)*0.5; int cnt=0;
    while (prt<1) {prt*=10; cnt--;}
    while (prt>10) {prt*=0.1; cnt++;}
    printf("%.9lfe", prt);
    if (cnt>=0) printf("+%03d\n", cnt);
    else printf("%03d\n",cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/81036707