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;
} 
posted @ 2024-06-04 20:56  长安一片月_22  阅读(9)  评论(0编辑  收藏  举报