P1442 铁球落地(线段树dp)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1442 铁球落地(线段树dp)相关的知识,希望对你有一定的参考价值。
P1442 铁球落地(线段树dp)
比较显然的是从下往上dp,对每个平台 i i i考虑是从左边转移还是右边转移。
因为从两端下落的平台 j j j是一定的。转移方程很好写。关键是如何找到 j j j。
从下往上考虑,我们对已经遍历过的区间进行覆盖,这样就变成区间修改,单点查询了。上线段树就好了。
注意区间值域较大,需要离散化。
然后dp的时候到地面特判,高度就是 a [ i ] . h a[i].h a[i].h。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码参考题解区
#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行
以上是关于P1442 铁球落地(线段树dp)的主要内容,如果未能解决你的问题,请参考以下文章
HDU 4521 小明系列问题——小明序列 (线段树维护DP)
AtCoder abc256全题解(区间合并模板矩阵快速幂优化dp线段树……)