luogu P1442铁球落地题解

Posted quitter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P1442铁球落地题解相关的知识,希望对你有一定的参考价值。

铁球落地

题目描述

N(n≤100000)个平台上空有一个铁球,球每次落到某个平台上后,游戏者可以选择向左或向右滚,球滚动和落下的速度都是1。由于铁球的质量不太好,每次落下的高度不能超过MAX。设计一种策略,使得球尽快落到地面而不被摔碎。假设地面高度为0,且无限宽。

技术图片

输入格式

第一行是两个数n,max。

第二行两个数,分别表示铁球起始位置的横纵坐标。

接下来n行,第i行是三个正整数hi,li,ri,

输入数据保证有解,且平台的高度互不相同、各边缘与横坐标的值均互不相同。

输出格式

仅一个数,为铁球到达地面的最短时间。

输入输出样例

输入 #1
5 3
6 10
5 2 4
9 3 9
6 7 10
2 1 5
3 8 11

这题应该不难想到使用dp求解。
我们先将每块木板按照高度排序。
然后我们按照从矮到高的顺序倒序遍历这些木板。
可能你会问为什么要从矮到高的顺序dp。
其实从高到到矮的顺序dp,其实从高到矮并不是不可以。
但从低到高会来的更方便。
因为从低到高的话,我们只需要考虑从左边下落到达的木板,和从右边下落到达的两个木板继承就可以了。
然而从高到低我们并不方便找到哪些下落可以到达该木板。
以上都是废话

假若我们已经求得了这个木板左右下落会到达哪些木板,
我们记 ch[i][1/0] 0表示i的左边下落到达的木板,1表示右边下落到达的木板。
    dp[i][1/0] 0表示如果下落到左边木板,最小费时,1表示下落到右边木板的最小费时。
    la[i] 表示 i 的右端点横坐标, ra[i] 表示 i 的左端点横坐标。
    a[i].h 记录 i 木板的高度。
dp 方程如下: 

  dp[i][0]=min(dp[i][0],dp[ch[i][0]][0]+la[i]-la[ch[i][0]]+a[i].h-a[ch[i][0]].h);
  dp[i][0]=min(dp[i][0],dp[ch[i][0]][1]+ra[ch[i][0]]-la[i]+a[i].h-a[ch[i][0]].h);

  dp[i][1]=min(dp[i][1],dp[ch[i][1]][0]+ra[i]-la[ch[i][1]]+a[i].h-a[ch[i][1]].h);
  dp[i][1]=min(dp[i][1],dp[ch[i][1]][1]+ra[ch[i][1]]-ra[i]+a[i].h-a[ch[i][1]].h);

其实这后面转移就是路径的模拟,不难明白。

 

dp方程解决了,还有一个问题,那就是如何处理出每个木板左右下落后到达的木板?

一个比较好理解的方案是线段树。

 

这里使用了区间赋值和单点修改

我们从低到高枚举木板,查询左/右端点下线段树上对应的值。

这值就是我们要求的落到的那个木板编号。

当这个木板完后,他就是对应区间(木板的左端点到右端点),最上面的木板了。

它上面的应当会落在它的上面。

于是区间修改。把这段区间的值修改为这个木板的编号。

到此,问题得到完美解决

 

等等,我们千万不要忘记离散化,以及dp时特判。

如果当前的木板下落到的木板编号为0,那么dp是当前木板的高度。

 

具体看代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls k<<1
#define rs k<<1|1
typedef long long ll;
using namespace std;
const int N=1e6;
ll n,m,cnt;
ll lx[N<<2],ch[N][2],w[N<<4],tag[N<<4];//
ll la[N<<2],ra[N<<2],dp[N][2];//
struct node{
    ll l,r,h,id;//需要记录一个编号,因为需要排序
    bool operator < (const node &b) const {
        return h<b.h;//按高度从矮到高排序
    }
}a[N];
inline void read(ll &x){
    x=0;
    char ch=getchar();
    while(ch<0||ch>9) ch=getchar();
    while(ch>=0&&ch<=9) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}
inline void update(ll k,ll x,ll y){
    tag[ls]=tag[k];
    w[ls]=tag[k];
    tag[rs]=tag[k];
    w[rs]=tag[k];
    tag[k]=0;
}
inline void change(ll k,ll x,ll y,ll l,ll r,ll val){
    if(x>r||y<l) return;
    if(x>=l&&y<=r){//区间赋值
        tag[k]=val;
        w[k]=val;
        return;
    }
    ll mid=x+y>>1;
    if(tag[k]) update(k,x,y);//区间赋值的标记下传
    change(ls,x,mid,l,r,val);
    change(rs,mid+1,y,l,r,val);
}
inline ll query(ll k,ll x,ll y,ll pos){
    if(x>pos||y<pos) return 0;
    if(x==y) return w[k];//单点查询,w记录当前区间对应最上方的木板编号
    ll mid=x+y>>1;
    if(tag[k]) update(k,x,y);
    return query(ls,x,mid,pos)+query(rs,mid+1,y,pos);
}
int main()
{
    ll i,j,stx,sty;
    read(n),read(m);
    read(stx),read(sty);
    n++;//有一个起始位置
    a[n].l=a[n].r=stx;
    a[n].h=sty,a[n].id=n;
    la[n]=stx,ra[n]=stx;
    lx[++cnt]=a[n].l;
    for(i=1;i<n;i++){
        read(a[i].h),read(a[i].l),read(a[i].r);
        a[i].id=i;
        la[i]=a[i].l;
        ra[i]=a[i].r;//la[] ra[]记录原来i木板的左右端点位置,因为需要离散化。
        lx[++cnt]=a[i].l;//lx[] 为离散化数组
        lx[++cnt]=a[i].r;
    }
    sort(lx+1,lx+cnt+1);//离散化数组排序
    sort(a+1,a+1+n);//木板高度排序
    for(i=1;i<=n;i++){//区间离散化
        a[i].l=lower_bound(lx+1,lx+1+cnt,a[i].l)-lx;
        a[i].r=lower_bound(lx+1,lx+1+cnt,a[i].r)-lx;
    }
    for(i=1;i<=n;i++){//点查询,不再多说
        ch[i][0]=query(1,1,cnt,a[i].l);
        ch[i][1]=query(1,1,cnt,a[i].r);
        change(1,1,cnt,a[i].l,a[i].r,i);
    }
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=dp[0][1]=0;//显而易见的初始化
    for(i=1;i<=n;i++){
        if(a[i].h-a[ch[i][0]].h<=m){//如果大于m不能转移
            if(ch[i][0]){//编号为0这带到到达地面
                 dp[i][0]=min(dp[i][0],dp[ch[i][0]][0]+la[a[i].id]-la[a[ch[i][0]].id]+a[i].h-a[ch[i][0]].h);
                 dp[i][0]=min(dp[i][0],dp[ch[i][0]][1]+ra[a[ch[i][0]].id]-la[a[i].id]+a[i].h-a[ch[i][0]].h);//加了离散数组的方程,一点点小变化
             }
             else dp[i][0]=a[i].h;
        }
        if(a[i].h-a[ch[i][1]].h<=m){
            if(ch[i][1]){
                dp[i][1]=min(dp[i][1],dp[ch[i][1]][0]+ra[a[i].id]-la[a[ch[i][1]].id]+a[i].h-a[ch[i][1]].h);
                dp[i][1]=min(dp[i][1],dp[ch[i][1]][1]+ra[a[ch[i][1]].id]-ra[a[i].id]+a[i].h-a[ch[i][1]].h);
            }
            else dp[i][1]=a[i].h;
        }
    }
    printf("%d",min(dp[n][0],dp[n][1]));//最后答案为转移到起始位置 n 的值。
}//刚好99行

 

以上是关于luogu P1442铁球落地题解的主要内容,如果未能解决你的问题,请参考以下文章

题解:琪露诺的冰雪小屋luogu3693

Luogu P1351 联合权值 题解

luogu P3950 部落冲突 题解

luogu_1550题解打井(最小生成树)

题解 Luogu P3370

luogu题解 P5020 货币系统