「JOISC 2019 Day3」穿越时空 Bitaro

题目

点这里看题目。

分析

一些简单的转化:

  • 其一,将原先的区间 \([L,R]\) 都变成 \([L,R-1]\) ;
  • 其二,将 \([L_i,R_i]\) 变成 \([L_i-i,R_i-i]\) ,这样就不需要考虑时间的自然流逝了。

考虑一个简单的贪心:维护当前的时间 \(t\) 。如果下一段路的区间为 \([L,R]\) ,那么若 \(t>R\) ,我们就必须使用技能移到 \(R\) 然后通过;若 \(t<L\) ,我们就必须等到 \(L\) 然后通过;否则可以直接通过。

继续考虑多个区间叠加的情况。可以发现只会有两种可能:

  1. 存在一段区间 \([l, r]\) ,满足当 \(t\in [l,r]\) 时可以直接穿过这段区间。

  2. 不存在这样的 \([l,r]\) ,那么我们总可以认为穿过这段区间的效果是:初始时刻 \(a\) ,到达时刻 \(b\) ,必须使用 \(k\) 次技能。

    这是因为我们可以在过区间的时候,将穿过后的一些技能平移到穿过前,这样我们的初始时刻就是相同的;同样也可以将穿过前的平移到穿过后,这样到达时刻就是相同的。

注意到两种情况时可以合并的,只是有亿点点复杂,所以我们就可以使用线段树维护一段区间的情况。

小结:

  1. 将情况分类讨论的灵活处理比较有价值,有的时候没有必要(也不能)强行归为一类。

代码

#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef long long LL;

const int MAXN = 3e5 + 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' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

struct Period
{
	int a, b; LL t; bool c;
	Period() { a = - 1e9, b = 1e9, c = t = 0; }
	Period( const int L, const int R ) { a = L, b = R, c = t = 0; }
	Period( int A, int B, bool C, LL T ) { a = A, b = B, c = C, t = T; }

	LL operator () ( const LL sta ) const 
	{
		if( c ) return t + ( sta > a ? sta - a : 0 );
		return t + ( sta > b ? sta - b : 0 );
	}
};

Period operator + ( const Period &x, const Period &y )
{
	Period ret;
	if( ! x.c && ! y.c )
	{
		if( MAX( x.a, y.a ) <= MIN( x.b, y.b ) )
			ret = Period( MAX( x.a, y.a ), MIN( x.b, y.b ), 0, x.t + y.t );
		else
		{
			if( y.b < x.a ) ret = Period( x.a, y.b, 1, x.t + y.t + x.a - y.b );
			else ret = Period( x.b, y.a, 1, x.t + y.t );
		}
	}
	else if( ! x.c )
	{
		if( x.b < y.a ) ret = Period( x.b, y.b, 1, x.t + y.t );
		else if( y.a < x.a ) ret = Period( x.a, y.b, 1, x.t + y.t + x.a - y.a );
		else ret = y;
	}
	else if( ! y.c )
	{
		if( y.b < x.b ) ret = Period( x.a, y.b, 1, x.t + y.t + x.b - y.b );
		else if( x.b < y.a ) ret = Period( x.a, y.a, 1, x.t + y.t );
		else ret = x;
	}
	else ret = Period( x.a, y.b, 1, x.t + y.t + ( x.b > y.a ? x.b - y.a : 0 ) );
	return ret;
}

struct Tree
{
	Period per[MAXN << 2];
	
	void Upt( const int x ) { per[x] = per[x << 1] + per[x << 1 | 1]; }
	
	void Update( const int x, const int l, const int r, const int p, const Period nw )
	{
		if( l == r ) return void( per[x] = nw );
		int mid = l + r >> 1;
		if( p <= mid ) Update( x << 1, l, mid, p, nw );
		else Update( x << 1 | 1, mid + 1, r, p, nw );
		Upt( x );
	}
	
	Period Query( const int x, const int l, const int r, const int segL, const int segR )
	{
		if( segL > segR ) return Period();
		if( segL <= l && r <= segR ) return per[x];
		int mid = l + r >> 1; Period ret;
		if( segL <= mid ) ret = ret + Query( x << 1, l, mid, segL, segR );
		if( mid < segR ) ret = ret + Query( x << 1 | 1, mid + 1, r, segL, segR );
		return ret;
	}
}nor, rev;

int L[MAXN], R[MAXN];
int N, Q;

int main()
{
	read( N ), read( Q );
	rep( i, 1, N - 1 )
	{
		read( L[i] ), read( R[i] ), R[i] --;
		nor.Update( 1, 1, N - 1, i, Period( L[i] - i, R[i] - i ) );
		rev.Update( 1, 1, N - 1, N - i, Period( L[i] - N + i, R[i] - N + i ) );
	}
	int opt, A, B, C, D;
	while( Q -- )
	{
		read( opt );
		if( opt == 1 )
		{
			read( A ), read( B ), read( C ), C --;
			nor.Update( 1, 1, N - 1, A, Period( B - A, C - A ) );
			rev.Update( 1, 1, N - 1, N - A, Period( B - N + A, C - N + A ) );
		}
		else
		{
			Period res; LL ans = 0;
			read( A ), read( B ), read( C ), read( D );
			if( A < C ) 
				res = nor.Query( 1, 1, N - 1, A, C - 1 ), B -= A, D -= C;
			else 
				res = rev.Query( 1, 1, N - 1, N - A + 1, N - C ), B -= N - A + 1, D -= N - C + 1;

			if( res.c )
			{
				ans = res.t;
				if( B > res.a ) ans += B - res.a;
				B = res.b;
				if( B > D ) ans += B - D;
			}
			else
			{
				ans = res.t;
				if( B < res.a ) B = res.a;
				else if( B > res.b ) ans = B - res.b, B = res.b;
				if( B > D ) ans += B - D;
			}
			write( ans ), putchar( '\n' );
		}
	}
	return 0;
}
上一篇:JavaWeb——QueryRunner


下一篇:Javascript 扫描摄像头或图像文件二维码和条形码