poj2374
题目大意
每一行有一个栅栏,在(n,s)位置有一群牛要到(0,0)位置去,他们无法翻越栅栏,遇到栅栏只能从两侧绕开,求他们横向移动的最短距离
题目分析
朴素dp:考虑用f[i][0]和f[i][1]分别表示到达i栅栏的左边和右边的时候的最短距离,暴力查找上一个栅栏即可。
线段树优化:由一点点贪心的思想可知,绕路的时候,从一个栅栏的两侧往前走,在没有走到另一个栅栏面前时,可以不横向移动。所以,我们可以从终点往起点处理,线段树维护每一个坐标上上一个栅栏的编号,对于每一个栅栏i,我们更新f[i][0]和f[i][1]的方法是在线段树里查找由i号栅栏两侧往下走会撞到哪个栅栏。接下来更新,比i更加靠近起点的栅栏在a[i]~b[i](i栅栏两侧)这样一段里面往下走都会撞到i栅栏。
不懂的看代码吧,本蒟蒻语死早。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
using namespace std;
const int N=50005;
int a[N],b[N],tr[800005],f[N][2];
int n,m,lim=100001,ans;
void pd(int i){
tr[i<<1]=tr[(i<<1)|1]=tr[i];
tr[i]=0;
}
int query(int x,int s,int t,int i){//单点查询
if(s==t)return tr[i];
if(tr[i])pd(i);
int mid=(s+t)>>1;
if(x<=mid)return query(x,s,mid,i<<1);
else return query(x,mid+1,t,(i<<1)|1);
}
void add(int l,int r,int s,int t,int i,int x){//区间修改
if(l<=s&&t<=r){tr[i]=x;return;}
if(tr[i])pd(i);
int mid=(s+t)>>1;
if(l<=mid)add(l,r,s,mid,i<<1,x);
if(mid+1<=r)add(l,r,mid+1,t,(i<<1)|1,x);
}
int main(){
int i,j;
scanf("%d%d",&n,&m);
a[0]=b[0]=lim;m+=lim;
for(i=1;i<=n;++i){
scanf("%d%d",&a[i],&b[i]);
a[i]+=lim,b[i]+=lim;
j=query(a[i],1,lim<<1,1);//dp值的更新
f[i][0]=min(f[j][0]+abs(a[j]-a[i]),f[j][1]+abs(b[j]-a[i]));
j=query(b[i],1,lim<<1,1);
f[i][1]=min(f[j][0]+abs(a[j]-b[i]),f[j][1]+abs(b[j]-b[i]));
add(a[i],b[i],1,lim<<1,1,i);
}
ans=min(f[n][0]+abs(a[n]-m),f[n][1]+abs(b[n]-m));
printf("%d\n",ans);
return 0;
}
HDU3016
题目大意
一个人有100血量,站在最高的板子上想要跳到地面,只能往板子两边跳。每到一块板子i,血量就会增加w[i],如果其血量小于等于0,这个人就死在半路了。求其落到地面的最多血量。
题目分析
将板子按照高度排序后,从最高的板子开始模拟。
线段树维护从每一个坐标跳下“带来”的血量的最大值。
首先对于一块板子i,查找l~r内的最大值x,则这块板子的dp值为x+w[i]。然后,将l~r都变成-inf,代表l~r这一段是不可能跳下来的(因为都被板子i“截住”了),接着,将l和r两个坐标处赋值为x+w[i](前提是x+w[i]>0,这个人没有死掉)。
其余的看代码吧。。。
注意一点:遇到一块dp值为0的板子就立刻输出-1是很错误的行为。。。我。。。我才没有犯过这种错误呢
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
using namespace std;
#define LL long long
const int N=100005;
int n;LL ans,inf=1e9+7;
LL f[N],sum[N<<2],laz[N<<2];
struct node{int h,l,r;LL val;}s[N];
bool cmp(node x,node y){return x.h>y.h;}
void pd(int i){
int l=i<<1,r=(i<<1)|1;
if(laz[i]!=0){
sum[l]=laz[i],sum[r]=laz[i];
laz[l]=laz[i],laz[r]=laz[i];
laz[i]=0;
}
}
void chan(int l,int r,int s,int t,int i,LL num){//区间修改
if(l<=s&&t<=r){sum[i]=laz[i]=num;return;}
pd(i);
int mid=(s+t)>>1;
if(l<=mid)chan(l,r,s,mid,i<<1,num);
if(mid+1<=r)chan(l,r,mid+1,t,(i<<1)|1,num);
sum[i]=max(sum[i<<1],sum[(i<<1)|1]);
}
LL query(int l,int r,int s,int t,int i){//区间查询
if(l<=s&&t<=r)return sum[i];
pd(i);
int mid=(s+t)>>1;LL re=-inf;
if(l<=mid)re=query(l,r,s,mid,i<<1);
if(mid+1<=r)re=max(re,query(l,r,mid+1,t,(i<<1)|1));
return re;
}
void build(int s,int t,int i){//初始化
if(s==t){sum[i]=-inf;return;}
int mid=(s+t)>>1;
build(s,mid,i<<1),build(mid+1,t,(i<<1)|1);
sum[i]=max(sum[i<<1],sum[(i<<1)|1]);
}
int main(){
freopen("lx.in","r",stdin);
int i,lim=100000;
while(~scanf("%d",&n)){
for(i=1;i<=n;++i)scanf("%d%d%d%lld",&s[i].h,&s[i].l,&s[i].r,&s[i].val);
sort(s+1,s+1+n,cmp);ans=0;
memset(sum,0,sizeof(sum));
f[1]=100+s[1].val;
build(1,lim,1);
chan(s[1].l,s[1].l,1,lim,1,f[1]),chan(s[1].r,s[1].r,1,lim,1,f[1]);
for(i=2;i<=n;++i){
f[i]=query(s[i].l,s[i].r,1,lim,1)+s[i].val;
chan(s[i].l,s[i].r,1,lim,1,-inf);
if(f[i]>0){
chan(s[i].l,s[i].l,1,lim,1,f[i]);
chan(s[i].r,s[i].r,1,lim,1,f[i]);
}
}
ans=sum[1];
if(ans<0)ans=-1;
printf("%lld\n",ans);
}
return 0;
}