#zkw線段樹,掃描線,dp,離散#NOIP2020.9.26模擬speike
阿新 • • 發佈:2020-10-14
分析
由於可以走邊界,那麼最短路徑一定按橫座標遞增並且經過矩形的頂點,
考慮掃描線,找到當前線段(矩形右邊界可以忽略)兩個端點離的最近而又可達的線段,
dp一下並用線段樹維護就可以了
程式碼
#include <cstdio> #include <cctype> #include <algorithm> #define rr register using namespace std; const int N=500011; typedef long long lll; lll dp[N][2]; struct duan{int x,l,r;}line[N]; int w[N<<2],xt,q,b[N<<1],bas,n,m; inline signed iut(){ rr int ans=0,f=1; rr char c=getchar(); while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans*f; } bool cmp(duan x,duan y){return x.x<y.x;} inline signed max(int a,int b){return a>b?a:b;} inline lll min(lll a,lll b){return a<b?a:b;} inline signed aabs(int x){return x<0?-x:x;} inline signed query(int x){ rr int ans=1; for (x+=bas;x;x>>=1) ans=max(ans,w[x]); return ans; } inline void update(int l,int r,int z){ for(l+=bas-1,r+=bas+1;l^r^1;l>>=1,r>>=1){ if (!(l&1)) w[l^1]=max(w[l^1],z); if (r&1) w[r^1]=max(w[r^1],z); } } signed main(){ freopen("speike.in","r",stdin); freopen("speike.out","w",stdout); n=iut(),m=1,line[1]=(duan){0,0,0}, line[2]=(duan){xt=iut(),0,0},q=2; for (rr int i=1;i<=n;++i){ rr int lx=iut(),ly=iut(),rx=iut(),ry=iut(); if (lx>rx) swap(lx,rx); if (ly>ry) swap(ly,ry); line[++q]=(duan){lx,ly,ry},b[++m]=ly,b[++m]=ry; } sort(b+1,b+1+m),m=unique(b+1,b+1+m)-b-1; sort(line+2,line+1+q,cmp); for (bas=1;(bas<<=1)<m+3;); for (rr int i=1;i<=q;++i) line[i].l=lower_bound(b+1,b+1+m,line[i].l)-b, line[i].r=lower_bound(b+1,b+1+m,line[i].r)-b; for (rr int i=2;i<=q;++i){ rr int t1=query(line[i].l),t2=query(line[i].r); dp[i][0]=min(dp[t1][0]+aabs(b[line[i].l]-b[line[t1].l]),dp[t1][1]+aabs(b[line[i].l]-b[line[t1].r])), dp[i][1]=min(dp[t2][0]+aabs(b[line[i].r]-b[line[t2].l]),dp[t2][1]+aabs(b[line[i].r]-b[line[t2].r])), update(line[i].l,line[i].r,i); } return !printf("%lld",min(dp[q][0],dp[q][1])+xt); }