acwing329 围栏障碍训练场 题解
考试压轴题,意识到这题是线段树优化 \(dp\) 时追悔莫及。
为了简化题目,我将从起点到原点变成了从原点到起点(这样就可以省去两个数组的空间)。
想到设 \(dp_{i,j}\) 表示在第 \(i\) 层,奶牛们在 \(j\) 列时的最小移动范围,则转移方程为(设输入为 \(l,r\)):
\[\begin{cases}
dp_{i,j}=dp_{i-1,j}\ (j\ne l,r)\\
dp_{i,l}=\min(dp_{i-1,l},\min\limits_{k=l+1}^{r-1}(dp_{i-1,k}+k-l))\\
dp_{i,r}=\min(dp_{i-1,r},\min\limits_{k=l+1}^{r-1}(dp_{i-1,k}+r-k))
\end{cases}
\]
发现可以通过线段树优化 \(dp\),时间复杂度 \(O(n\log_2 2\times 10^5)\)。
#include<bits/stdc++.h>
#define ll long long
#define ls(x) x*2
#define rs(x) x*2+1
using namespace std;
const int N=2e6+5;
int n,s,lb[N];
int lmn[N],rmn[N];
void push_down(int x){
if(!lb[x]) return;
lb[ls(x)]=lb[rs(x)]=1;
lmn[ls(x)]=rmn[ls(x)]=1e9;
lmn[rs(x)]=rmn[rs(x)]=1e9;
lb[x]=0;return;
}void build(int x,int l,int r){
lmn[x]=rmn[x]=1e9;
if(l==r) return;
build(ls(x),l,(l+r-1)/2);
build(rs(x),(l+r-1)/2+1,r);
}void add(int x,int l,int r,int a,int k){
if(l==r){
lb[x]=0;lmn[x]=min(lmn[x],k);
rmn[x]=min(rmn[x],k);return;
}push_down(x);int m=(l+r-1)/2;
if(a<=m) add(ls(x),l,m,a,k);
else add(rs(x),m+1,r,a,k);
lmn[x]=min(lmn[ls(x)],lmn[rs(x)]+m-l+1);
rmn[x]=min(rmn[ls(x)]+r-m,rmn[rs(x)]);
}int lmin(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return lmn[x];
push_down(x);int m=(l+r-1)/2;
if(R<=m) return lmin(ls(x),l,m,L,R);
if(L>m) return lmin(rs(x),m+1,r,L,R);
int lm=lmin(ls(x),l,m,L,R);
int rm=lmin(rs(x),m+1,r,m+1,R);
return min(lm,rm+m-L+1);
}int rmin(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return rmn[x];
push_down(x);int m=(l+r-1)/2;
if(R<=m) return rmin(ls(x),l,m,L,R);
if(L>m) return rmin(rs(x),m+1,r,L,R);
int lm=rmin(ls(x),l,m,L,m);
int rm=rmin(rs(x),m+1,r,L,R);
return min(lm+R-m,rm);
}void turn(int x,int l,int r,int L,int R){
if(L<=l&&r<=R){
lmn[x]=rmn[x]=1e9;
lb[x]=1;return;
}push_down(x);int m=(l+r-1)/2;
if(L<=m) turn(ls(x),l,m,L,R);
if(R>m) turn(rs(x),m+1,r,L,R);
}int que(int x,int l,int r,int a){
if(l==r) return lmn[x];
int m=(l+r-1)/2;
if(a<=m) return que(ls(x),l,m,a);
return que(rs(x),m+1,r,a);
}int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>s;
build(1,-1e5,1e5);
add(1,-1e5,1e5,0,0);
for(int i=1;i<=n;i++){
int l,r;cin>>l>>r;
if(l+2>r) continue;
int lm=lmin(1,-1e5,1e5,l+1,r-1);
int rm=rmin(1,-1e5,1e5,l+1,r-1);
turn(1,-1e5,1e5,l+1,r-1);
add(1,-1e5,1e5,l,lm+1);
add(1,-1e5,1e5,r,rm+1);
}int lm=lmin(1,-1e5,1e5,s+1,1e5);
int rm=rmin(1,-1e5,1e5,-1e5,s-1);
add(1,-1e5,1e5,s,min(lm,rm)+1);
cout<<que(1,-1e5,1e5,s);
return 0;
}