标签:war 影响 端点 直线 ++i 复杂度 for ide rom
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 2900 | Accepted: 1042 |
Description
+-S-+-+-+ (fence #N)
+-+-+-+ (fence #N-1)
... ...
+-+-+-+ (fence #2)
+-+-+-+ (fence #1)
=|=|=|=*=|=|=| (barn)
-3-2-1 0 1 2 3
Input
Output
Sample Input
4 0 -2 1 -1 2 -3 0 -2 1
Sample Output
4
Hint
+-+-S-+ Fence 4
+-+-+-+ Fence 3
+-+-+-+ Fence 2
+-+-+-+ Fence 1
|=|=|=*=|=|=| Barn
-3-2-1 0 1 2 3
Source
参照逐梦起航-带梦飞翔的题解,线段树优化DP
设f[i][0/1]表示在通过第i条栅栏后,处于栅栏左边/右边的最小路径长。
因为奶牛是直线下来的,所以最优方案当然是从上一个栅栏的这个位置下来。由于有栅栏的影响,奶牛们不能顺利的下来,此时到达这个位置的最优策略要么是从前面那个栅栏的左端点过来,要么从右端点过来。所以有
\[
f[i][0]=\min\{f[j][0]+|l_i-l_j|,f[j][1]+|l_i-r_j|\} \f[i][1]=\min\{f[j][0]+|r_i-l_j|,f[j][1]+|r_i-r_j|\}
\]
其中的j就是上一个挡住了这个位置的栅栏。我们可以用线段树来维护这个栅栏的编号。当栅栏(l[i],r[i]),出现后,我们把线段树上(l[i],r[i])这段区间改成i,表示这个位置是栅栏i阻挡了。对于后面的栅栏,修改时直接覆盖前面的信息即可。我们只要实现一个改段求点的线段树即可。
特别的,线段树初始值为0。一个位置如果得到的j=0,那么说明它前面没有栅栏,它可以直接从s过来,路径=abs(s-p)。
时间复杂度\(O(n\log s)\),也可以用线段树连边跑最短路,但这题用DP来做常数小。
#include<iostream>
#include<cmath>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int N=5e4+1,S=2e5+1,X=1e5;
int n,s,l[N],r[N],f[N][2];
struct T {int l,r,x;}t[S*4];
void build(int p,int l,int r){
t[p].l=l,t[p].r=r,t[p].x=l==r?0:-1;
if(l==r) return;
int mid=l+r>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
}
void change(int p,int l,int r,int x){
if(l<=t[p].l&&t[p].r<=r) return t[p].x=x,void();
if(t[p].x!=-1) t[p<<1].x=t[p<<1|1].x=t[p].x,t[p].x=-1;
int mid=t[p].l+t[p].r>>1;
if(l<=mid) change(p<<1,l,r,x);
if(r>mid) change(p<<1|1,l,r,x);
}
int ask(int p,int x){
if(t[p].l==t[p].r) return t[p].x;
if(t[p].x!=-1) t[p<<1].x=t[p<<1|1].x=t[p].x,t[p].x=-1;
int mid=t[p].l+t[p].r>>1;
return ask(x<=mid?p<<1:p<<1|1,x);
}
int main(){
read(n),read(s);
build(1,0,X*2);
s+=X,l[0]=r[0]=X;
for(int i=1,w;i<=n;++i){
l[i]=read<int>()+X,r[i]=read<int>()+X;
w=ask(1,l[i]);
f[i][0]=min(f[w][0]+abs(l[i]-l[w]),f[w][1]+abs(l[i]-r[w]));
w=ask(1,r[i]);
f[i][1]=min(f[w][0]+abs(r[i]-l[w]),f[w][1]+abs(r[i]-r[w]));
change(1,l[i],r[i],i);
}
printf("%d\n",min(f[n][0]+s-l[n],f[n][1]+r[n]-s));
return 0;
}
标签:war 影响 端点 直线 ++i 复杂度 for ide rom
原文地址:https://www.cnblogs.com/autoint/p/10794492.html