poj2374 Fence Obstacle Course(DP)(線段樹)
阿新 • • 發佈:2018-12-13
題目
題解
線段樹優化DP 設f[i][0/1]表示在通過第i條柵欄後,處於柵欄左邊/右邊的最小路徑長。 因為奶牛是直線下來的,所以最優方案當然是從上一個柵欄的這個位置下來。由於有柵欄的影響,奶牛們不能順利的下來,此時到達這個位置的最優策略要麼是從前面那個柵欄的左端點過來,要麼從右端點過來。所以有,。 其中的j就是上一個擋住了這個位置的柵欄。我們可以用線段樹來維護這個柵欄的編號。當柵欄(l[i],r[i]),出現後,我們把線段樹上(l[i],r[i])這段區間改成i,表示這個位置是柵欄i阻擋了。對於後面的柵欄,修改時直接覆蓋前面的資訊即可。我們只要實現一個改段求點的線段樹即可。 特別的,線段樹初始值為0。一個位置如果得到的j=0,那麼說明它前面沒有柵欄,它可以直接從s過來,路徑=abs(s-p)。
程式碼
#include<cstdio> #include<cstring> #include<algorithm> #define rt 1,1,tot using namespace std; const int maxn=50010,maxtr=maxn*2*4; int hh[maxtr],la[maxtr]; void update(int x) { if(la[x]==-1) return ; int lc=x<<1,rc=lc|1; hh[lc]=hh[rc]=la[x]; la[lc]=la[rc]=la[x]; la[x]=-1; } void change(int x,int xl,int xr,int l,int r,int c) { if(xl==l && xr==r) { hh[x]=la[x]=c; return ; } int mid=xl+xr>>1; int lc=x<<1,rc=lc|1; update(x); if(r<=mid) change(lc,xl,mid,l,r,c); else if(mid<l) change(rc,mid+1,xr,l,r,c); else change(lc,xl,mid,l,mid,c),change(rc,mid+1,xr,mid+1,r,c); } int ask(int x,int xl,int xr,int p) { if(xl==xr) { return hh[x]; } int mid=xl+xr>>1; int lc=x<<1,rc=lc|1; update(x); if(p<=mid) return ask(lc,xl,mid,p); else return ask(rc,mid+1,xr,p); } int l[maxn],r[maxn]; int a[maxn*2];int tot=0; int ul[maxn],ur[maxn]; int f[maxn][2]; int main() { int n,s; scanf("%d%d",&n,&s); for(int i=n;i>=1;i--) { scanf("%d%d",&l[i],&r[i]); a[++tot]=l[i];a[++tot]=r[i]; } a[++tot]=0;a[++tot]=s; sort(a+1,a+tot+1);tot=unique(a+1,a+tot+1)-(a+1); for(int i=1;i<=n;i++) ul[i]=lower_bound(a+1,a+tot+1,l[i])-a,ur[i]=lower_bound(a+1,a+tot+1,r[i])-a; for(int i=1;i<=n;i++) { int fl=ask(rt,ul[i]),fr=ask(rt,ur[i]); if(fl==0) f[i][0]=abs(s-l[i]); else f[i][0]=min(f[fl][0]+abs(l[fl]-l[i]),f[fl][1]+abs(r[fl]-l[i])); if(fr==0) f[i][1]=abs(s-r[i]); else f[i][1]=min(f[fr][0]+abs(l[fr]-r[i]),f[fr][1]+abs(r[fr]-r[i])); change(rt,ul[i],ur[i],i); } int e=lower_bound(a+1,a+tot+1,0)-a; int ff=ask(rt,e); if(ff==0) printf("%d\n",abs(s)); else printf("%d\n",min(f[ff][0]+abs(l[ff]),f[ff][1]+abs(r[ff]))); return 0; }