USACO 2020 January Contest, Gold T3 SpringBoards
阿新 • • 發佈:2020-08-01
Bessie 在一個僅允許沿平行於座標軸方向移動的二維方陣中。她從點 \((0,0)\) 出發,想要到達 \((N,N)(1≤N≤10^9)\)。為了幫助她達到目的,在方陣中有 \(P(1≤P≤10^5)\)個跳板。每個跳板都有其固定的位置 \((x_1,y_1)\),如果 Bessie 使用它,會落到點 \((x_2,y_2)\)。
Bessie 是一個過程導向的奶牛,所以她僅允許她自己向上或向右行走,從不向左或向下。類似地,每個跳板也設定為不向左或向下。Bessie 需要行走的距離至少是多少?
一開始我還以為是什麼最短路問題,後來感覺建圖過於毒瘤不太對頭。。。。仔細一想不就是一個樹狀陣列優化的DP嘛!
先來考慮樸素狀態轉移,令\(f[i]\)為只使用前\(i\)個跳板,且一定使用了第\(i\)個跳板,所省下的最大距離。
則\(f[i]=max\{f[j]+x2[i]-x1[i]+y2[i]-y1[i]\},x2[j]<=x1[i],y2[j]<=y1[i]\)。
而由於我們選定了\(i\),可以把\(f[i]\)和\(x2[i],y2[i]\)一起考慮。
這個問題就被轉化為了類似二維數點問題,然而二維樹狀陣列鐵定MLE,因此我們可以降維:
將每個跳板兩頭點混合在一起成為陣列\(a[]\),以\(x\)為第一關鍵字、\(y\)為第二關鍵字排序,順序訪問所有點,即實現了\(x\)的單調。那麼就成了\(y\)
遇到起點就更新\(f[跳板號]\),也就是樹狀陣列下標\(a[i].y\)字首的最大值+這個跳板能越過的距離。
遇到終點就將\(f[跳板號]\)加入樹狀陣列,也就是\(a[i].y\)後更新最大值。
需要注意的是座標範圍過大,需要先離散化。這裡新建了一個儲存\(y\)座標的陣列\(b[]\),之後再映射回\(a[i].y\)即可。
#include<bits/stdc++.h> using namespace std; const int MAXN=2e5+5; //起點終點一起處理,雙倍大小 int n,p,tot,ntot,ans; int b[MAXN],tree[MAXN],f[MAXN],dis[MAXN]; //分別是:縱座標暫時儲存、樹狀陣列、DP陣列、單個跳板省下的距離 struct node{ int x,y,chk,id; // 座標、頂點性質、跳板號 bool operator < (const node &A)const{ if(x!=A.x) return x<A.x; if(y!=A.y) return y<A.y; return chk<A.chk; } }a[MAXN]; void add(int k,int x) // 樹狀陣列新增結點更新最大值 { for(;k<=tot;k+=(k&-k)) tree[k]=max(tree[k],x); } int ask(int k) // 樹狀陣列查詢字首 { int ret=0; for(;k;k-=(k&-k)) ret=max(ret,tree[k]); return ret; } int main() { freopen("boards.in","r",stdin); freopen("boards.out","w",stdout); cin>>n>>p; for(int i=1;i<=p;i++) { int x,y; cin>>x>>y; a[++tot]=(node){x,y,0,i}; b[tot]=y; dis[i]-=x+y; cin>>x>>y; a[++tot]=(node){x,y,1,i}; b[tot]=y; dis[i]+=x+y; // 計算dis陣列,為之後計算f[i]做準備 } a[++tot]=(node){n,n,2,0}; sort(b+1,b+tot+1); ntot=unique(b+1,b+tot+1)-b-1; // 離散化縱座標 for(int i=1;i<=tot;i++) a[i].y=lower_bound(b+1,b+ntot+1,a[i].y)-b; sort(a+1,a+tot+1); for(int i=1;i<=tot;i++) { if(a[i].chk==2) { for(int j=1;j<=p;j++) if(a[i].x>=a[j].x && a[i].y>=a[j].y) ans=max(ans,f[j]); printf("%d\n",2*n-ans); //不用跳板是2n,所以答案是2n-ans return 0; } if(!a[i].chk) f[a[i].id]=ask(a[i].y)+dis[a[i].id]; // 起點就更新 else add(a[i].y,f[a[i].id]); // 終點就增加 } return 0; }