题目
点这里看题目。
分析
离线的话,我们显然可以 线段树分治 + DP ,时间复杂度大概是 (O(mlog_2m+mp)) 。
不过,既然题目明确要求在线,却还不开强制在线,我们就应该去思考一下在线算法。
显然我们需要一个 DP 去维护答案,这里不再赘述。
考虑我们直接处理的难点之一是双端队列两段可操作,而一端可操作的结构,栈,就可以简单地维护 DP 。因此,我们考虑将双端队列拆成两栈分别维护左端和右端。
有了这个思路,剩下的都比较简单了。插入操作可以直接维护 DP 数组。删除操作可以把栈顶的 DP 数组清空。查询的时候,我们枚举一端的 DP 值 (dpl(i)) ,此时对应可用的 (dpr) 的值必然是一段区间,我们可以考虑使用单调队列之类的数据结构维护一下。
插入、查询都可以做到 (O(p)) 。删除时,如果一端的栈已经被删空了,我们需要在另一端的栈里打上标记,在之后对两个栈进行均分重构。
时间复杂度是 ...... (O(? imes 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( '
' );
}
return 0;
}