1. 程式人生 > 實用技巧 >「雅禮集訓 2018 Day10」貪玩藍月

「雅禮集訓 2018 Day10」貪玩藍月

題目

點這裡看題目。

分析

離線的話,我們顯然可以 線段樹分治 + 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;
}