题:https://atcoder.jp/contests/abc177/tasks/abc177_f
题意:给定n和m,代表有(n+1)*m的矩阵,有n个询问,每个询问给定a和b,代表在第 i 行的第a列到第b列,不能进行向下操作,其余位置可以进行向下或向右操作,问在给定的限制中,从第1行的任意位置走到第i+1行的最短距离是多少或输出-1表示不能到达。
分析:对于每个位置的答案,我们建立线段树来记录答案。考虑某个位置pos的答案,肯定是从上一行的左边的某个位置sta能够整体地向右移动pos-sta,再向下移动一个单位,同时保证移动到pos位置是最小的。
因为这个最小还与位置有关,所以我们再建一棵关于ansi-i的线段树;
我们可以发现,若能得到终点是a~b的路径,那么答案肯定有比这些路径还小的答案,(a以前),所以每次就把a~b的位置加上无穷大,代表不会取到,其次,若1~b能够到达下一行的b+1~n,那么只需更新b+1位置即可,因为后面位置若通过此方法进行移动,肯定比b+1位置大,所以不用更新。
最后整体加上1表示向下移动了一步
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r const int M=2e5+5; const int inf=0x3f3f3f3f; const ll INF=1e18; struct Segtree{ ll tr[M<<2],lz[M<<2]; void build(int root,int l,int r){ tr[root]=0,lz[root]=0; if(l==r) return ; int midd=(l+r)>>1; build(lson); build(rson); } void pushdown(int root){ ll c=lz[root]; tr[root<<1]+=c,tr[root<<1|1]+=c; lz[root<<1]+=c,lz[root<<1|1]+=c; lz[root]=0; } void update(int L,int R,ll val,int root,int l,int r){ if(L<=l&&r<=R){ tr[root]+=val; lz[root]+=val; return ; } if(lz[root]) pushdown(root); int midd=(l+r)>>1; if(L<=midd) update(L,R,val,lson); if(R>midd) update(L,R,val,rson); tr[root]=min(tr[root<<1],tr[root<<1|1]); } ll query(int L,int R,int root,int l,int r){ if(L<=l&&r<=R){ return tr[root]; } if(lz[root]) pushdown(root); ll res=INF; int midd=(l+r)>>1; if(L<=midd) res=min(res,query(L,R,lson)); if(R>midd) res=min(res,query(L,R,rson)); return res; } }x,y; int main(){ int n,m; scanf("%d%d",&n,&m); x.build(1,1,m),y.build(1,1,m); for(int i=1;i<=m;i++) y.update(i,i,-i,1,1,m); for(int i=1;i<=n;i++){ int a,b; scanf("%d%d",&a,&b); ///b+1 x.update(b+1,b+1,min(1ll*(b+1+y.query(1,b,1,1,m))-x.query(b+1,b+1,1,1,m),0ll),1,1,m); y.update(b+1,b+1,min(y.query(1,b,1,1,m)-y.query(b+1,b+1,1,1,m),0ll),1,1,m); ///a~b x.update(a,b,1e9,1,1,m); y.update(a,b,1e9,1,1,m); ///向下 x.update(1,m,1,1,1,m); y.update(1,m,1,1,1,m); ll ans=x.query(1,m,1,1,m); if(ans>=1e9) puts("-1"); else printf("%lld ",ans); } return 0; }