题目大意:
给出一些篱笆,编号为1~n,1号篱笆在最下面,n号篱笆在最上面,给出起点s的坐标,要求牛从起点出发,到达到达n号篱笆下面的地上的0点,求出最小横向路径
分析:
dp方程比较好想,大概就是没一个篱笆可以更新它下面的最近的一个可以直达的篱笆,还是看代码吧:
f[1][0]=labs(s-a[1]),f[1][1]=labs(b[1]-s);
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++)
if(a[i]>=a[j]&&a[i]<=b[j]){
f[j][0]=min(f[j][0],f[i][0]+labs(a[j]-a[i]));
f[j][1]=min(f[j][1],f[i][0]+labs(a[i]-b[j]));
break;
}
for(int j=i+1;j<=n;j++)
if(b[i]>=a[j]&&b[i]<=b[j]){
f[j][0]=min(f[j][0],f[i][1]+labs(b[i]-a[j]));
f[j][1]=min(f[j][1],f[i][1]+labs(b[j]-b[i]));
break;
}
}
然后我们发现这是O(n^2)的转移,n<=5W,挂了
然后我们发现,对于每个i所对应的j可以用线段树来维护,大概可以简化为区间覆盖问题:倒着插入区间,更新区间id,查询每个篱笆的左右端点被哪个id覆盖
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define ZERO 100005
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=50000+5,maxm=200000+5;
int n,s,a[maxn],b[maxn],f[maxn][2],MAX,MIN;
struct Tree{
int l,r,id;
}tree[maxm*4];
struct M{
int A,B;
}S[maxn];
inline void build(int l,int r,int tr){
tree[tr].l=l,tree[tr].r=r,tree[tr].id=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(l,mid,tr<<1),build(mid+1,r,tr<<1|1);
}
inline void change(int l,int r,int id,int tr){
if(tree[tr].l==l&&tree[tr].r==r){
tree[tr].id=id;
return;
}
int mid=(tree[tr].l+tree[tr].r)>>1;
if(tree[tr].id!=-1){
tree[tr<<1].id=tree[tr].id;
tree[tr<<1|1].id=tree[tr].id;
}
if(r<=mid)
change(l,r,id,tr<<1);
else if(l>mid)
change(l,r,id,tr<<1|1);
else
change(l,mid,id,tr<<1),change(mid+1,r,id,tr<<1|1);
if(tree[tr<<1].id!=tree[tr<<1|1].id||tree[tr<<1].id==-1||tree[tr<<1|1].id==-1)
tree[tr].id=-1;
}
inline int query(int pos,int tr){
if(tree[tr].l==tree[tr].r||(tree[tr].l<=pos&&tree[tr].r>=pos&&tree[tr].id!=-1))
return tree[tr].id;
int mid=(tree[tr].l+tree[tr].r)>>1;
if(tree[tr].id!=-1){
tree[tr<<1].id=tree[tr].id;
tree[tr<<1|1].id=tree[tr].id;
}
if(pos>mid)
return query(pos,tr<<1|1);
else
return query(pos,tr<<1);
}
signed main(void){
scanf("%lld%lld",&n,&s),MAX=-inf,MIN=inf;
memset(f,inf,sizeof(f));
for(int i=n;i>=1;i--)
scanf("%lld%lld",&a[i],&b[i]),MAX=max(MAX,b[i]),MIN=min(a[i],MIN);
f[1][0]=labs(s-a[1]),f[1][1]=labs(b[1]-s);
build(ZERO+MIN,MAX+ZERO,1);
for(int i=n;i>=1;i--)
S[i].A=query(a[i]+ZERO,1),S[i].B=query(b[i]+ZERO,1),change(a[i]+ZERO,b[i]+ZERO,i,1);
for(int i=1;i<n;i++){
int j=S[i].A;
if(j!=0)
f[j][0]=min(f[j][0],f[i][0]+labs(a[j]-a[i])),f[j][1]=min(f[j][1],f[i][0]+labs(a[i]-b[j]));
j=S[i].B;
if(j!=0)
f[j][0]=min(f[j][0],f[i][1]+labs(b[i]-a[j])),f[j][1]=min(f[j][1],f[i][1]+labs(b[j]-b[i]));
}
int ans=min(f[n][0]+labs(a[n]),f[n][1]+labs(b[n]));
for(int i=1;i<n;i++){
if(S[i].A==0)
ans=min(f[i][0]+labs(a[i]),ans);
if(S[i].B==0)
ans=min(ans,f[i][1]+labs(b[i]));
}
cout<<ans<<endl;
return 0;
}
by >_< neighthorn

本文针对一个关于牛穿越多个篱笆到达目的地的问题,提出了从O(n^2)的时间复杂度优化到使用线段树进行区间覆盖查询的方法,显著提高了求解效率。
360

被折叠的 条评论
为什么被折叠?



