ICPC WF 연습 (3/7)

Flip Flow

모래시계를 시뮬레이션 하는 문제이다.

시키는 대로 구현하면 된다.

Ascent Sequences

다음 조건을 모두 만족하는 길이 NN의 수열 {ai}\{ a_i \}의 가짓수를 구하는 문제이다:

  • 모든 원소는 음이 아닌 정수이다.
  • ai≤Ai−1+1a_i \le A_{i-1} + 1. 여기서, AiA_i는 a1⋯aia_1 \cdots a_i에서 증가한 횟수.
  • "201 패턴"이 존재하면 안된다.

대충 다음과 같은 생각을 하였다:

  • 문제에서 찾고자 하는 수열은 다음 두 수열을 적당히 합쳤다고 생각해도 된다:
    • aia_i는 max⁡(a1,a2,⋯ ,ai−1)\max \left( a_1, a_2, \cdots, a_{i-1} \right) 혹은 이보다 하나 작은 값.
    • 계단 형태로 감소하는 수열
  • 각 수열 모두 적당한 DP로 해결할 수 있다. 나는 O(N4)O\left( N^4 \right) 크기의 DP를 생각하였다.
  • 이제 적당히 합쳐서 가짓수를 세면 된다.

하지만, 결국 풀지 못했다. 더 많은 관찰이 필요한 듯 하다.

Buggy Combination Lock

두 개의 수열 {ai}\{ a_i \}, {bi}\{ b_i \}가 있다. 다음과 같은 연산을 사용하여, 수열 {ai}\{ a_i \}를 {bi}\{ b_i \}로 바꿀 때, 연산 사용의 최소 횟수를 구하는 문제이다.

  • aia_i과 ai+1a_{i+1}에 모두 11을 더하거나, −1-1을 더한다.
    • Index는 modulo NN, 원소의 값은 modulo MM에서 생각한다.

Index ii에 대하여 적용한 값을 viv_i라고 하자. −M<vi<M-M < v_i < M이며, bi=ai+vi+vi−1b_i = a_i + v_i + v_{i-1}를 만족하면서 ∑i∣min⁡(vi,M−vi)∣\sum_i \left \lvert \min \left( v_i, M - v_i \right) \right \rvert가 최소가 되는 {vi}\{ v_i \}를 찾으면 된다.

식을 조금 변형하면, vi−1+vi=bi−ai=:civ_{i-1} + v_i = b_i - a_i =: c_i를 얻을 수 있다. 즉, vi=Aiv0+Biv_i = A_i v_0 + B_i 형태로 나타낼 수 있으며, 또한 Ai=±1A_i = \pm 1가 성립한다.

이제, Case를 나누자:

  • A0=1A_0 = 1인 경우,
    • B0≠0B_0 \ne 0이라면, 해가 존재하지 않는다.
    • B0=0B_0 = 0이라면, 모든 v0v_0에 대해 해가 존재한다. (즉, 해가 총 MM개 존재한다.)
      • f(v0):=∑i∣min⁡(vi,M−vi)∣f\left( v_0 \right) := \sum_i \left \lvert \min \left( v_i, M - v_i \right) \right \rvert라고 하자.
      • 함수 ff는 NN개의 Piecewise-linear function의 합으로 표현할 수 있다.
      • 따라서, ff의 모든 local minima와 maxima를 전부 알아낼 수 있다.
      • 즉, min⁡f\min f를 O(Nlg⁡N)O \left( N \lg N \right)에 구할 수 있다.
  • A0=−1A_0 = -1인 경우,
    • v0=−v0+B0v_0 = -v_0 + B_0, 2v0=B02 v_0 = B_0이므로, 가능한 v0v_0의 값은 최대 두 개이다.
    • 가능한 모든 v0v_0에 대해, ∑i∣min⁡(vi,M−vi)∣\sum_i \left \lvert \min \left( v_i, M - v_i \right) \right \rvert의 값을 계산하면 충분하다.

따라서, O(Nlg⁡N)O\left( N \lg N \right)의 시간 복잡도로 답을 계산할 수 있다.

틀리기 쉬운 요소가 상당히 많았고, 나는 그런 부분에 대해 모두 한 번씩 틀렸었다.

Cyclic Shifts

문자열 S=s1s2⋯skS= s_1 s_2 \cdots s_k에 대해, 문자열 si+1⋯sks1⋯sis_{i+1} \cdots s_k s_1 \cdots s_i가 사전순으로 최소가 되는 ii 중, 가장 작은 ii를 Cyclic shift라고 정의하자. 길이 NN의 문자열 SS가 주어질 때, 모든 Prefix에 대하여 Cyclic shift의 값을 구하는 문제이다.

찾아보니 논문이다. 길이 1,2,⋯ ,K1, 2, \cdots, K인 Prefix의 Cyclic shift 값을 모두 알고 있을 때, 단 세 번의 추가 비교 연산만으로 K+1K+1-Prefix의 Cyclic shift 값을 알아낼 수 있다고 한다.

Exclusive Training

NN개의 벡터 (ri,pi,ai,bi)\left( r_i, p_i, a_i, b_i \right)가 주어진다. 각 ii에 대하여, 다음 최댓값을 구하는 문제이다:

  • r≤rir \le r_i, ai≤d≤bia_i \le d \le b_i인 (r,d)(r, d)를 고정하자.
  • rj≤rr_j \le r, aj≤d≤bja_j \le d \le b_j를 모두 만족하는 모든 j≠ij \ne i에 대하여, ∑jpj\sum_j p_j의 최댓값.

먼저, rir_i가 서로 다르므로, rir_i의 오름차순으로 정렬하자.

Key가 dd인 Segment Tree를 생각하자. 이 Segment Tree는 다음 연산을 지원한다:

  • 구간에 pip_i 더하기.
  • 각 dd에 대하여, 지금까지 자신이 가졌던 값의 최댓값을 mx(d)mx(d)라고 하자. 구간에서 mx(d)mx(d)의 최댓값 구하기.

rir_i가 작은 ii부터 차례대로 보면서, 구간 [ai,bi]\left[ a_i, b_i \right]에서 최댓값이 문제의 답이 된다. 문제의 답을 구한 후, 구간 [ai,bi]\left[ a_i, b_i \right]에 pip_i를 더해주면 된다.

Segment Tree의 이러한 연산은 Lazy Propagation Technique를 이용하면 효율적으로 처리할 수 있다. Propagation 정보로

  • 구간에 더해주어야 하는 값
  • 이 구간에 더해주어야 하는 값의 Prefix sum의 최댓값 (mx(d)mx(d)에 더해주어야 하는 값)

을 들고 있어야 한다.

전체 시간 복잡도는 O(Nlg⁡N)O \left( N \lg N \right), 공간 복잡도는 O(N)O \left( N \right)이다.

상당히 재미있는 테크닉이다.