2 条题解
-
0
全网最啰嗦题解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
全网最啰嗦题解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
信息
- ID
- 2227
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 9
- 标签
- 递交数
- 12
- 已通过
- 3
- 上传者