「雅礼集训 2018 Day10」贪玩蓝月

题目

点这里看题目。

分析

离线的话,我们显然可以 线段树分治 + DP ,时间复杂度大概是 \(O(m\log_2m+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;
}
posted @ 2020-08-09 16:48  crashed  阅读(276)  评论(0编辑  收藏  举报