[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;
}