[NOI2020省选]冰火战士

题目

点这里看题目。

分析

\(F(T)\)为温度为\(T\)的时候火系战士能量和,\(I(T)\)\(T\)时冰系战士能量和。

显然我们需要求:

\[\max\{\min\{F(T),I(T)\}\} \]

另一个显然的事情是,\(F(T)\)是一个后缀和,\(I(T)\)是一个前缀和;因而\(F(T)\)单减,\(I(T)\)单增。

那么\(\min\{F(T),I(T)\}\)就应该是一个有最高平台的函数。

更进一步的,设\(T_1=\max\{T|I(T)\le F(T)\},T_2=\min\{T|F(T)<I(T)\}\),那么我们可以知道,峰要么是\(I(T_1)\),要么是\(F(T_2)\)

首先我们可以离散化后,用树状数组维护一下\(I\)的前缀和和\(F\)前缀差和。计算\(F\)的时候,我们用能量和减掉范围外的。

寻找\(T_1\),我们可以直接在树状数组上面二分。具体来说,我们实际上枚举步长来进行倍增,并利用树状数组得到当前的前缀信息。

寻找\(F(T_2)\),我们可以直接找出\(T_1\)的后继\(T_2'\),那么\(F(T_2)=F(T_2')\)。不过,有可能\(T_2'<T_2\),我们需要在树状数组上面把\(F(T_2')\)的值代入,二分找出\(T_2\)

所以总共应该有两个二分,一个寻找\(T_1\),一个寻找\(T_2\)

时间复杂度是\(O(n\log_2n)\),非常非常卡,需要卡常或者 O2 。

代码

#include <cmath>
#include <cstdio>
#include <algorithm>

const int MAXN = 2e6 + 5;

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' );
}

struct oper
{
	int type, id, x, y;
}op[MAXN];

int F[MAXN], I[MAXN];
int temp[MAXN];
int typ[MAXN], T[MAXN], E[MAXN];
int Q, N, lg2, all;

void up( int &x ) { x += x & ( -x ); }
void down( int &x ) { x &= ( x - 1 ); }
void uptF( int x, const int v ) { for( ; x <= N ; up( x ) ) F[x] += v; }
void uptI( int x, const int v ) { for( ; x <= N ; up( x ) ) I[x] += v; }
int getF( int x ) { int ret = 0; for( ; x ; down( x ) ) ret += F[x]; return ret; }
int getI( int x ) { int ret = 0; for( ; x ; down( x ) ) ret += I[x]; return ret; }

int find1()
{
	int p = 0, ice = 0, fire = 0, tmp;
	for( int s = 1 << lg2 ; s ; s >>= 1 )
	{
		tmp = p + s;
		if( tmp > N ) continue;
		ice += I[tmp];
		fire += F[tmp];
		if( ice <= all - fire ) p = tmp;
		else ice -= I[tmp], fire -= F[tmp];
	}
	return p;
}

int find2( const int tar )
{
	int p = 0, fire = 0, tmp;
	for( int s = 1 << lg2 ; s ; s >>= 1 )
	{
		tmp = p + s;
		if( tmp > N ) continue;
		fire += F[tmp];
		if( all - fire >= tar ) p = tmp;
		else fire -= F[tmp];
	}
	return p;
}

int main()
{
	read( Q );
	for( int i = 1 ; i <= Q ; i ++ )
	{
		read( op[i].type ), read( op[i].id );
		if( op[i].type == 1 ) 
			read( op[i].x ), read( op[i].y ), temp[++ N] = op[i].x;
	}
	std :: sort( temp + 1, temp + 1 + N );
	N = std :: unique( temp + 1, temp + 1 + N ) - temp - 1;
	lg2 = log2( N ); int pos, p1, e1, p2, e2;
	for( int i = 1 ; i <= Q ; i ++ )
	{
		if( op[i].type == 1 )
		{
			pos = std :: lower_bound( temp + 1, temp + 1 + N, op[i].x ) - temp;
			if( op[i].id ) all += op[i].y, uptF( pos + 1, op[i].y );
			else uptI( pos, op[i].y );
			typ[i] = op[i].id, T[i] = pos, E[i] = op[i].y;
		}
		else
		{
			pos = op[i].id;
			if( typ[pos] ) all -= op[pos].y, uptF( T[pos] + 1, -E[pos] );
			else uptI( T[pos], - E[pos] );
		}
		p1 = find1(), e1 = getI( p1 );
		p2 = p1 == N ? N : p1 + 1, e2 = all - getF( p2 );
		if( e1 == 0 && e2 == 0 ) { puts( "Peace" ); continue; }
		if( e1 > e2 ) write( temp[p1] ), putchar( ' ' ), write( e1 * 2ll ), putchar( '\n' );
		else write( temp[find2( e2 )] ), putchar( ' ' ), write( e2 * 2ll ), putchar( '\n' );
	}
	return 0;
}
posted @ 2020-07-05 13:54  crashed  阅读(186)  评论(0编辑  收藏  举报