「雅禮集訓 2018 Day10」貪玩藍月
阿新 • • 發佈:2020-08-09
題目
點這裡看題目。
分析
離線的話,我們顯然可以 線段樹分治 + DP ,時間複雜度大概是 \(O(q\log_2q+mp)\) 。
不過,既然題目明確要求線上,卻還不開強制線上,我們就應該去思考一下線上演算法。
顯然我們需要一個 DP 去維護答案,這裡不再贅述。
考慮我們直接處理的難點之一是雙端佇列兩段可操作,而一端可操作的結構,棧,就可以簡單地維護 DP 。因此,我們考慮將雙端佇列拆成兩棧分別維護左端和右端。
有了這個思路,剩下的都比較簡單了。插入操作可以直接維護 DP 陣列。刪除操作可以把棧頂的 DP 陣列清空。查詢的時候,我們列舉一端的 DP 值 \(dpl(i)\) ,此時對應可用的 \(dpr\)
插入、查詢都可以做到 \(O(p)\) 。刪除時,如果一端的棧已經被刪空了,我們需要在另一端的棧裡打上標記,在之後對兩個棧進行均分重構。
時間複雜度是 ...... \(O(?\times p)\) 。
程式碼
#include <cstdio> #include <cstring> typedef long long LL; const LL INF = 1e10; const int MAXN = 5e4 + 5, MAXP = 505; template<typename _T> void read( _T &x ) { x = 0;char s = getchar();int f = 1; while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();} while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();} x *= f; } template<typename _T> void write( _T x ) { if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; } if( 9 < x ){ write( x / 10 ); } putchar( x % 10 + '0' ); } template<typename _T> _T MAX( const _T a, const _T b ) { return a > b ? a : b; } LL DPL[MAXN][MAXP], DPR[MAXN][MAXP]; LL f[MAXP << 1]; int q[MAXP]; int stkL[MAXN], stkR[MAXN]; int tmp[MAXN]; int w[MAXN], v[MAXN]; int N, P, topL, topR, butL, butR; void rebuild(); void upt( LL &x, const LL v ) { x = MAX( x, v ); } int fix( const int a ) { return ( a % P + P ) % P; } void popL() { if( ! topL ) { butR ++; return ; } topL --; if( ! topL ) rebuild(); } void popR() { if( ! topR ) { butL ++; return ; } topR --; if( ! topR ) rebuild(); } void insertL( const int id ) { w[id] %= P, stkL[++ topL] = id; for( int i = 0 ; i < P ; i ++ ) DPL[topL][i] = -INF; for( int i = 0 ; i < P ; i ++ ) upt( DPL[topL][i], DPL[topL - 1][i] ), upt( DPL[topL][( i + w[id] ) % P], DPL[topL - 1][i] + v[id] ); } void insertR( const int id ) { w[id] %= P, stkR[++ topR] = id; for( int i = 0 ; i < P ; i ++ ) DPR[topR][i] = -INF; for( int i = 0 ; i < P ; i ++ ) upt( DPR[topR][i], DPR[topR - 1][i] ), upt( DPR[topR][( i + w[id] ) % P], DPR[topR - 1][i] + v[id] ); } void rebuild() { int siz = 0; for( int i = topL ; i > butL ; i -- ) tmp[++ siz] = stkL[topL]; for( int i = butR + 1 ; i <= topR ; i ++ ) tmp[++ siz] = stkR[i]; butL = butR = topL = topR = 0; int mid = siz >> 1; for( int i = mid ; i ; i -- ) insertL( tmp[i] ); for( int i = mid + 1 ; i <= siz ; i ++ ) insertR( tmp[i] ); } LL query() { if( butL || butR ) rebuild(); int L, R; LL ans = -INF; int h = 1, t = 0, r = 0; read( L ), read( R ); for( int i = 0 ; i < P ; i ++ ) f[i] = f[i + P] = DPR[topR][i]; for( int i = L ; ~ i ; i -- ) { for( ; r <= fix( R - i ) ; r ++ ) { while( h <= t && f[q[t]] <= f[r] ) t --; q[++ t] = r; } while( h <= t && q[h] < L - i ) h ++; ans = MAX( ans, f[q[h]] + DPL[topL][i] ); } r = 0; for( int i = P - 1 ; i > L ; i -- ) { for( ; r <= R - i + P ; r ++ ) { while( h <= t && f[q[t]] <= f[r] ) t --; q[++ t] = r; } while( h <= t && q[h] < L - i + P ) h ++; ans = MAX( ans, f[q[h]] + DPL[topL][i] ); } return ans < 0 ? -1 : ans; } int main() { int qwq; read( qwq ); char op[10]; read( N ), read( P ); for( int i = 1 ; i < P ; i ++ ) DPL[0][i] = DPR[0][i] = -INF; for( int cas = 1 ; cas <= N ; cas ++ ) { scanf( "%s", op ); if( ! strcmp( op, "IF" ) ) read( w[cas] ), read( v[cas] ), insertL( cas ); if( ! strcmp( op, "IG" ) ) read( w[cas] ), read( v[cas] ), insertR( cas ); if( ! strcmp( op, "DF" ) ) popL(); if( ! strcmp( op, "DG" ) ) popR(); if( ! strcmp( op, "QU" ) ) write( query() ), putchar( '\n' ); } return 0; }