题目
点这里看题目。
分析
一些简单的转化:
- 其一,将原先的区间 \([L,R]\) 都变成 \([L,R-1]\) ;
- 其二,将 \([L_i,R_i]\) 变成 \([L_i-i,R_i-i]\) ,这样就不需要考虑时间的自然流逝了。
考虑一个简单的贪心:维护当前的时间 \(t\) 。如果下一段路的区间为 \([L,R]\) ,那么若 \(t>R\) ,我们就必须使用技能移到 \(R\) 然后通过;若 \(t<L\) ,我们就必须等到 \(L\) 然后通过;否则可以直接通过。
继续考虑多个区间叠加的情况。可以发现只会有两种可能:
-
存在一段区间 \([l, r]\) ,满足当 \(t\in [l,r]\) 时可以直接穿过这段区间。
-
不存在这样的 \([l,r]\) ,那么我们总可以认为穿过这段区间的效果是:初始时刻 \(a\) ,到达时刻 \(b\) ,必须使用 \(k\) 次技能。
这是因为我们可以在过区间的时候,将穿过后的一些技能平移到穿过前,这样我们的初始时刻就是相同的;同样也可以将穿过前的平移到穿过后,这样到达时刻就是相同的。
注意到两种情况时可以合并的,只是有亿点点复杂,所以我们就可以使用线段树维护一段区间的情况。
小结:
- 将情况分类讨论的灵活处理比较有价值,有的时候没有必要(也不能)强行归为一类。
代码
#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;
}