문제
백준 13310
설명
특정 시점에서의 별들 간의 최대거리를 구하는 함수를 만들어 함수 값이 최소가 되는 시점을 구하는 Paramatric Search 문제이다. 별들 간의 최대거리는 회전하는 캘리버스 알고리즘을 알고있다면 간단히 구현할 수 있다. 문제는 T
의 범위가 커서 브루트포스로는 답을 구할 수 없다는 것이다. 이를 삼분탐색을 사용해 시간을 로그단위로 만드는 것이 핵심이다.
하지만 위의 전략은 Unimodal 한 함수에서만 가능한데 어떻게 이를 확신할 수 있을까?
이는 의외로 간단한데, \(\mathrm{N}\choose{2}\) 에 달하는 별들의 쌍 각각에 대한 시간에 따른 거리의 함수는 Unimodal 한 함수이다. 정확히는 V 모양을 가진 함수가 되겠다. V 모양 함수가 합쳐진다면 최솟값이 많아야 2개 이상일 것이다. 여기서 우리가 구하는 것이 거리의 최댓값인 것을 생각하면, 적어도 둘 중 한 함수의 최솟값에선 다른 함수의 값이 더 크므로 최소값은 하나가 된다는 것을 알 수 있다. 그래서 W 모양은 존재할 수 없다. 이러한 과정을 반복하여 우리가 사용할 함수를 만들 수 있으므로, 그 결과 역시 Unimodal 한 함수가 되는 것이다.
시간 복잡도
O(\(\mathrm{N} \log{\mathrm{T}}\))
코드
using ll = long long ;
template < typename ll = long long >
struct VEC {
ll x , y ;
VEC operator + ( const VEC & in ) const { return { x + in . x , y + in . y }; }
VEC operator - ( const VEC & in ) const { return { x - in . x , y - in . y }; }
bool operator < ( const VEC & in ) const { return x == in . x ? y < in . y : x < in . x ; }
bool operator <= ( const VEC & in ) const { return x == in . x ? y <= in . y : x <= in . x ; }
bool operator == ( const VEC & in ) const { return x == in . x && y == in . y ; }
ll Cross ( const VEC & in ) const { return x * in . y - y * in . x ; }
ll Dist2 ( const VEC & in ) const {
return ( x - in . x ) * ( x - in . x ) + ( y - in . y ) * ( y - in . y );
}
friend ll CCW ( const VEC & a , VEC b , VEC c )
{
b = b - a ;
c = c - a ;
return b . x * c . y - b . y * c . x ;
}
};
using ll = long long ;
using ii = VEC < ll > ;
ii arr [ 30001 ];
struct START {
ll x , y , dx , dy ;
} inputs [ 30001 ];
ll Calc ( int n , ll day )
{
for ( int i = 0 ; i < n ; i ++ )
arr [ i ] = { inputs [ i ]. x + ( ll ) inputs [ i ]. dx * day , inputs [ i ]. y + ( ll ) inputs [ i ]. dy * day };
n -= arr + n - unique ( arr , arr + n );
swap ( arr [ 0 ], arr [ min_element ( arr , arr + n ) - arr ]);
sort ( arr + 1 , arr + n , [ base = arr [ 0 ]]( ii & a , ii & b ) {
ll c = CCW ( base , a , b );
return c != 0 ? c > 0 : base . Dist2 ( a ) < base . Dist2 ( b );
});
deque < int > st ; st . push_back ( 0 ); st . push_back ( 1 );
for ( int i = 2 ; i < n ; i ++ )
{
while ( st . size () > 1 && 0 >= CCW ( arr [ st [ st . size () - 2 ]], arr [ st [ st . size () - 1 ]], arr [ i ]))
st . pop_back ();
st . push_back ( i );
}
int s = st . size ();
ll ans = max ( ans , arr [ st [ 0 ]]. Dist2 ( arr [ st [ 1 ]]));
for ( int l = 0 , r = 1 ; r != 0 ; )
{
ll theta = ( arr [ st [( l + 1 ) % s ]] - arr [ st [ l ]]). Cross ( arr [ st [ r ]] - arr [ st [( r + 1 ) % s ]]);
theta > 0 ? l = ( l + 1 ) % s : r = ( r + 1 ) % s ;
ans = max ( ans , arr [ st [ l ]]. Dist2 ( arr [ st [ r ]]));
}
return ans ;
}
int main ()
{
fastio ;
int N , T ;
cin >> N >> T ;
for ( int i = 0 ; i < N ; i ++ )
cin >> inputs [ i ]. x >> inputs [ i ]. y >> inputs [ i ]. dx >> inputs [ i ]. dy ;
int s = 0 , e = T ;
while ( e - s > 2 )
{
int d = ( e - s ) / 3 ;
int p = s + d , q = s + ( d << 1 );
ll pv = Calc ( N , p ), qv = Calc ( N , q );
pv > qv ? s = p + 1 : e = q - 1 ;
}
ll ans = 1e18 , ans_i = - 1 ;
for ( int i = s ; i <= e ; i ++ )
{
ll d = Calc ( N , i );
if ( d < ans )
{
ans = d ;
ans_i = i ;
}
}
cout << ans_i << '\n' << ans ;
}
댓글남기기