題意:一個老鼠在一條長度爲L的直線上跑,吃蛋糕,老鼠只能沿直線移動。開始時沒有蛋糕,老鼠的初始位置是0.ui
有兩個操做,0 x 表明在位置x添加一個蛋糕; 1 表明老鼠想吃蛋糕。老鼠每次都會選擇離本身最近的點,若是兩邊距離相同,老鼠優先選擇與本身當前移動方向相同的點。spa
求最終移動的總距離。code
題解:線段樹單點修改+查詢區間端點。blog
設當前位置爲pos,每次查詢區間[0, pos]的最右端和區間[pos, L]的最左端,比較選擇哪一個更近。ip
詳細題解見代碼註釋。string
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #define lson l, m, rt << 1 #define rson m+1, r, rt << 1 | 1 #define LEFT 0 #define RIGHT 1 using std::min; using std::max; using std::abs; const int MAXN = 100010; const int INF = 1 << 30; int tr[MAXN << 2]; //標記哪一個點有蛋糕 int left[MAXN << 2]; //標記區間[l, r]內有蛋糕的最左端點 int right[MAXN << 2]; //標記區間[l, r]內有蛋糕的最右端點 int N, S; int findL, findR; //標記查找到的任意區間[l, r]中有蛋糕的最左端和最右端 void PushUp( int rt ) { int lc = rt << 1; int rc = rt << 1 | 1; left[rt] = min( left[lc], left[rc] ); right[rt] = max( right[lc], right[rc] ); return; } void build( int l, int r, int rt ) //建樹 { tr[rt] = 0; //每一個點初始蛋糕數目爲0 left[rt] = INF; right[rt] = -INF; if ( l == r ) return; int m = ( l + r ) >> 1; build( lson ); build( rson ); return; } void Update( int pos, int v, int l, int r, int rt ) //更新 { if ( l == pos && r == pos ) { tr[rt] += v; //更新該點的蛋糕數目 if ( tr[rt] == 0 ) //若是這一點沒有蛋糕了 { left[rt] = INF; right[rt] = -INF; } else //若是這一點有蛋糕 { left[rt] = l; right[rt] = l; } return; } int m = ( l + r ) >> 1; if ( pos <= m ) Update( pos, v, lson ); else Update( pos, v, rson ); PushUp( rt ); return; } void Query( int L, int R, int l, int r, int rt ) //查詢 { if ( L <= l && r <= R ) { findL = min( left[rt], findL ); findR = max( right[rt], findR ); return; } int m = ( l + r ) >> 1; if ( L <= m ) Query( L, R, lson ); if ( R > m ) Query( L, R, rson ); return; } int main() { int T, cas = 0; scanf( "%d", &T ); while ( T-- ) { scanf( "%d%d", &N, &S ); ++N; //原題目中pipe長度爲L,編號爲0-L,其實是有(L+1)個點, //以前沒注意,RE了一次,在這裏我把每一個點編號爲1-(L+1) int curPos = 1; //當前位置 int curDirection = RIGHT; //當前方向 int sum = 0; //移動總路程 build( 1, N, 1 ); //建樹 while ( S-- ) { //printf("**************curPos=%d curDir=%d\n", curPos, curDirection ); int op, pos; scanf( "%d", &op ); if ( op == 0 ) { scanf( "%d", &pos ); Update( pos+1, 1, 1, N, 1 ); } else { bool find = false; int findpos; if ( curDirection == LEFT ) //若是當前方向向左 { int LL, RR; findL = INF; findR = -INF; Query( 1, curPos, 1, N, 1 ), LL = findR; //向左走,找離當前點最近的右端點 findL = INF; findR = -INF; Query( curPos, N, 1, N, 1 ), RR = findL; //向右走,找離當前點最近的左端點 if ( LL == -INF && RR == INF ) //兩邊都沒有蛋糕 { find = false; } else if ( LL == -INF && RR != INF ) //只有右邊有蛋糕 { find = true; findpos = RR; curDirection = RIGHT; } else if ( LL != -INF && RR == INF ) //只有左邊有蛋糕 { find = true; findpos = LL; curDirection = LEFT; } else //兩邊都有蛋糕 { find = true; if ( curPos - LL <= RR - curPos ) //注意等號,兩邊距離相等時優先選擇左邊 { findpos = LL; curDirection = LEFT; } else { findpos = RR; curDirection = RIGHT; } } //printf("LEFT: "); } else { int LL, RR; findL = INF; findR = -INF; Query( curPos, N, 1, N, 1 ), RR = findL; findL = INF; findR = -INF; Query( 1, curPos, 1, N, 1 ), LL = findR; if ( LL == -INF && RR == INF ) { find = false; } else if ( LL == -INF && RR != INF ) { find = true; findpos = RR; curDirection = RIGHT; } else if ( LL != -INF && RR == INF ) { find = true; findpos = LL; curDirection = LEFT; } else { find = true; if ( curPos - LL < RR - curPos ) //注意沒有等號,兩邊距離相等時優先選擇右邊 { findpos = LL; curDirection = LEFT; } else { findpos = RR; curDirection = RIGHT; } } //printf("RIGHT: "); } //printf( "findL=%d findR=%d\n", findL, findR ); //printf( "findpos = %d\n", findpos ); if ( find ) { sum += abs( findpos - curPos ); curPos = findpos; Update( findpos, -1, 1, N, 1 ); } } } printf( "Case %d: %d\n", ++cas, sum ); } return 0; }