2 条题解

  • 0
    @ 2025-10-8 17:00:39

    全网最啰嗦题解by hansang:

    #include <bits/stdc++.h> //题解 by hansang
    using namespace std;
    const int N=5e4+10, inf=1e5+10; //*重点 
    typedef long long LL;
    #define lp p<<1
    #define rp (p<<1)|1
    struct trnode{int l, r, c;} tr[inf<<3]; //总的范围是 inf*2,而线段树一般开 4倍 
    struct node{int l, r;} a[N];
    LL f[N][2]; int n, ml, mr;
    //f[i][0]表示从原点到第 i条线段左端的最小路径,f[i][1]则是右端 
    void bt(int p, int l, int r){
    	tr[p]={l, r, 0};
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	bt(lp, l, mid); bt(rp, mid+1, r);
    }
    void change(int p, int l, int r, int c){
    	if(tr[p].l>r || tr[p].r<l) return ; //不符合范围 
    	if(tr[p].l>=l && tr[p].r<=r) {tr[p].c=c; return ;}
    	//l  tr[p].l  tr[p].r  r 符合当前范围就修改,剩下的范围程序会在别的分支递归 
    	if(tr[p].c){
    		tr[lp].c=tr[rp].c=tr[p].c; //懒标记下发 
    		tr[p].c=0;
    	}
    	change(lp, l, r, c); change(rp, l, r, c); ;
    }
    int query(int p, int x){
    	if(tr[p].l>x || tr[p].r<x) return 0; //无解 
    	if(tr[p].l==tr[p].r) return tr[p].c;
    	if(tr[p].c){
    		tr[lp].c=tr[rp].c=tr[p].c; 
    		tr[p].c=0;
    	}
    	return max(query(lp, x), query(rp, x)); 
    	//无解的时候返回 0,有解的时候大于 0,这里输出较大值 
    } 
    int main(){
    	int S; scanf("%d%d", &n, &S);
    	memset(f, 0x3f, sizeof(f));
    	f[0][0]=f[1][0]=0;
    	a[0]={inf, inf}; ml=mr=inf; 
    	//数据范围有负数,线段树只能处理正数,所有数都应加上 inf 
    	
    	//大概思路是*倒序*寻找,从原点开始找,每一次循环找一条线段 x 
    	//条件满足可以从当前线段掉落到线段 x,用 dp记录 
    	/*
    	       x:     ------       |    ------    
    		当前:      ------     |   -----    可以从左端点掉落     可以从右端点掉落 
    		(只是举例,实际上有更多种符合掉落的情况) 
    	*/
    	for(int i=1; i<=n+1; i++){
    		if(i==n+1) a[i]={S, S}; 
    		else scanf("%d%d", &a[i].l, &a[i].r);
    		a[i].l+=inf; a[i].r+=inf;
    		ml=min(ml, a[i].l); //ml mr 是范围 
    		mr=max(mr, a[i].r);
    	}
    	bt(1, ml, mr); //建树 
    	
    	for(int i=1; i<=n+1; i++){
    		int al=query(1, a[i].l), ar=query(1, a[i].r);
    		//左右端点都找,找的离自己纵向距离最近的情况下横向距离最短的线段 
    		f[i][0]=min(f[al][0]+abs(a[i].l-a[al].l), f[al][1]+abs(a[i].l-a[al].r));
    		f[i][1]=min(f[ar][1]+abs(a[i].r-a[ar].r), f[ar][0]+abs(a[i].r-a[ar].l));
    		change(1, a[i].l, a[i].r, i); //dp完之后把当前线段也加进线段树 
    	}
    	printf("%lld\n", f[n+1][0]); //第 n+1个点就是(S, K+1),因为是倒序寻找 
    	return 0;
    }
    
    • 0
      @ 2025-10-8 17:00:17

      全网最啰嗦题解by hansang:

      #include<bits/stdc++.h> //题解 by hansang
      using namespace std;
      const int N=5e4+10, inf=1e5+10; //*重点 
      typedef long long LL;
      #define lp p<<1
      #define rp (p<<1)|1
      struct trnode{int l, r, c;} tr[inf<<3]; //总的范围是 inf*2,而线段树一般开 4倍 
      struct node{int l, r;} a[N];
      LL f[N][2]; int n, ml, mr;
      //f[i][0]表示从原点到第 i条线段左端的最小路径,f[i][1]则是右端 
      void bt(int p, int l, int r){
      	tr[p]={l, r, 0};
      	if(l==r) return ;
      	int mid=(l+r)>>1;
      	bt(lp, l, mid); bt(rp, mid+1, r);
      }
      void change(int p, int l, int r, int c){
      	if(tr[p].l>r || tr[p].r<l) return ; //不符合范围 
      	if(tr[p].l>=l && tr[p].r<=r) {tr[p].c=c; return ;}
      	//l  tr[p].l  tr[p].r  r 符合当前范围就修改,剩下的范围程序会在别的分支递归 
      	if(tr[p].c){
      		tr[lp].c=tr[rp].c=tr[p].c; //懒标记下发 
      		tr[p].c=0;
      	}
      	change(lp, l, r, c); change(rp, l, r, c);
      }
      int query(int p, int x){
      	if(tr[p].l>x || tr[p].r<x) return 0; //无解 
      	if(tr[p].l==tr[p].r) return tr[p].c;
      	if(tr[p].c){
      		tr[lp].c=tr[rp].c=tr[p].c; 
      		tr[p].c=0;
      	}
      	return max(query(lp, x), query(rp, x)); 
      	//无解的时候返回 0,有解的时候大于 0,这里输出较大值 
      } 
      int main(){
      	int S; scanf("%d%d", &n, &S);
      	memset(f, 0x3f, sizeof(f));
      	f[0][0]=f[1][0]=0;
      	a[0]={inf, inf}; ml=mr=inf; 
      	//数据范围有负数,线段树只能处理正数,所有数都应加上 inf 
      	
      	//大概思路是*倒序*寻找,从原点开始找,每一次循环找一条线段 x 
      	//条件满足可以从当前线段掉落到线段 x,用 dp记录 
      	/*
      	       x:     ------       |    ------    
      		当前:      ------     |   -----
      		    可以从左端点掉落     可以从右端点掉落 
      		(只是举例,实际上有更多种符合掉落的情况) 
      	*/
      	for(int i=1; i<=n+1; i++){
      		if(i==n+1) a[i]={S, S}; 
      		else scanf("%d%d", &a[i].l, &a[i].r);
      		a[i].l+=inf; a[i].r+=inf;
      		ml=min(ml, a[i].l); //ml mr 是范围 
      		mr=max(mr, a[i].r);
      	}
      	bt(1, ml, mr); //建树 
      	
      	for(int i=1; i<=n+1; i++){
      		int al=query(1, a[i].l), ar=query(1, a[i].r);
      		//左右端点都找,找的离自己纵向距离最近的情况下横向距离最短的线段 
      		f[i][0]=min(f[al][0]+abs(a[i].l-a[al].l), f[al][1]+abs(a[i].l-a[al].r));
      		f[i][1]=min(f[ar][1]+abs(a[i].r-a[ar].r), f[ar][0]+abs(a[i].r-a[ar].l));
      		change(1, a[i].l, a[i].r, i); //dp完之后把当前线段也加进线段树 
      	}
      	printf("%lld\n", f[n+1][0]); //第 n+1个点就是(S, K+1),因为是倒序寻找 
      	return 0;
      } 
      • 1

      0x50 动态规划(练习)18:[USACO04DEC]Fence Obstacle Course

      信息

      ID
      2227
      时间
      1000ms
      内存
      128MiB
      难度
      9
      标签
      递交数
      12
      已通过
      3
      上传者