티스토리 뷰

APIO

APIO 2019

arnold518 2019. 12. 27. 14:22

https://apio2019.ru/

https://oj.uz/problems/source/389

https://koosaga.com/232


내가 참가한 첫번째 apio 대회이다. 대회 결과는... 30점으로 매우 참혹한 점수가 나왔다.

나는 대회시간에 device 를 읽고 어려운 수학문제라 판단하여 기본적인 부분점수만 긁고 풀지 않았는데, 이것이 가장 쉬운 문제였다. 대신 나는 마지막 문제였던 lamps에 모든 시간을 쏟아부었고, 결국 완벽한 풀이를 찾은 후 2D segment tree 구현, 경로압축까지 구현을 끝냈는데 MLE가 나서 결국 이것도 섭태밖에 긁지 못했다. 나중에 다시 풀어보니 경로압축 없이 fenwick + dynamic segment tree 하나만 있어도 AC가 나왔다...

이번 년도의 문제들은 평소보다 비교적 쉬웠다고 할 수 있다. (특히 작년에 비해서)


1. Device


문제를 잘 읽어보면 x=t+[t/B] (mod A) , y=t (mod B) 일때 (x, y)를 세어야 한다.

가장 먼저 생각해 볼 것은 언젠가는 (x, y) 에 주기성이 생길 것이라는 것이다. 이제 이 주기만 구해보자.


y=t (mod B) 에서 t를 하나씩 증가하다보면 B를 주기로 y가 반복된다. 동시에 x에 [t/B] 항이 있는것으로 t가 B만큼 증가하면 이 값도 B를 주기로 1씩 증가한다는 것을 알 수 있다. 다시 말해, 정확히 B가 증가하면 x는 1+B가 증가한다.


(1+B)k mod A 가 0이 되는 최소의 k를 p라 한다면, 주기 q=pB 가 된다.

A|(1+B)p 이고, gcd(A, 1+B)=g라 하면 A/g | (1+B)/g p 이고, A/g, (1+B)/g 는 서로소이므로 p=A/g가 된다.


결론적으로, 찾고있는 주기는 A*B/gcd(A, 1+B) 가 된다. 이제 주기 T를 구했으니 답을 구해보자.


[0, T) 구간 위에 입력으로 주어지는 구간들을 색칠해 보자.

구간이 [0, T) 를 완전히 덮는다면 답은 T이고, 아니라면 l<=r 일 때, l>r일때로 나누어 각각 색칠해준 후, 마지막에 한번의 스위핑을 통해 답의 개수를 세주면 된다.


이때 주의해야할 것은 A, B는 10^18 이하이기 때문에 계산 과정에서 오버플로우가 날 수 있으니 이를 유의해야 한다.


시간 복잡도 : O(NlgN)


2. Lamps


먼저, 지금 당장 어떤 두 점이 연결되어 있는지를 어떻게 확인할 수 있을까? 연결된 가로등의 도로들은 하나의 구간을 이룬다. 이 구간을 [s, e]라 표현하고 이를 이차원 좌표평면상에 플로팅 하면 여러 개의 점들로 표현이 된다.


이제 이미 있는 구간 [l, r]에 대해서 a->b로 갈 수 있는지 확인하기 위해서는 [l, r]안에 [a, b]가 속하는지만 확인하면 된다. 이는 l<=a && b<=r, 즉 2차원 좌표평면 상에서는 a, b의 왼쪽 위쪽에 점이 있는지 확인하면 된다.


이제 이것을 이용하여 문제를 풀어 보자. 만약 처음 이후 가로등의 갱신 쿼리가 들어오지 않는다고 생각해보자. 처음에 있던 구간은 Q+1만큼의 시간 동안 성능을 발휘할 가능성이 있다. 이제 만약 어떤 점이 시간 t에 삭제된다고 생각하자. 원래는 Q+1만큼의 성능을 발휘할 가능성이 있었지만, 시간 t이후로는 성능이 없어지기 때문에 t만큼 빼준다고 생각하면 된다. 


답을 구할 때는 일단 그 점의 왼쪽 위의 점들의 합을 구한다. 그런데, 각 점들은 지금 남은 시간만큼의 가능성을 더 갖고 있기 때문에 이 값은 점의 수 * 남은 시간의 값이 추가로 더해져 있을 것이다. 이를 해결하기 위하여 단순히 점들의 값의 합 뿐만 아니라 점들의 수의 합도 구해서 같이 계산해주면 된다.


이와 같이 현재 남은 시간의 값을 점들에 더해주거나 빼주면 된다. 이제 필요한 연산은 N*N 평면에서 점 하나에 어떤 값들을 더해주거나 빼주고, 주어진 구간 안에 있는 점들의 값의 합을 구하는 것이다. 이는 2D segment tree를 이용하면 구현할 수 있다. 


하지만 메모리 제한을 초과한다. 이를 해결하기 위하여 2d segment tree를 최적화하는 여러 방법들을 사용해 보자. 

1. 바깥쪽 segment tree는 굳이 segment tree일 필요가 없다. 가장 효율적이고 구현도 짧은 Fenwick Tree로 바꾸자. -> AC!

2. 안쪽의 Dynamic segment tree 는 IOI 2013 game 의 경로 압축 트릭을 이용하자.

3. 포인터는 느리니 모두 배열로 고정 할당 한 다음 하나씩 배정해주자.


4. 아에 2D Segment Tree를 쓰지 말자!

오프라인 문제이고 2차원 평면 상의 합 쿼리이므로 분할정복과 Fenwick Tree 하나로 해결할 수 있다.


시간 복잡도 : O(Qlg^2N)



3. Bridges


가장 먼저 중요한 섭태 4를 풀어 보자.

업데이트가 없으니 전체 간선과 쿼리를 모두 가중치가 감소하는 순서대로 정렬한 후에, 하나씩 추가하면서 Union-Find 자료구조를 통하여 답을 구해주면 된다.


이제 업데이트가 있는 경우를 생각해 보자.

IOI Elephants, 단조성이 없는 CHT 와 같은 주저장소-부저장소 의 아이디를 이용하여 해결할 수 있다. 전체 쿼리를 B개의 묶음씩 나누고 생각하자.


지금 보고 있는 버킷 이전의 업데이트는 모두 처리했다면, 이번 버킷에서 업데이트되지 않는 간선들은 이전의 업데이트 결과를 그대로 유지할 것이다. 이들은 내림차순으로 정렬해놓고 섭태 4와 똑같이 해결하면 된다.


이제 지금 버킷의 쿼리도 마찬가지로 내림차순으로 정렬해놓고 문제를 해결해 나갈 것이다. 어떠한 쿼리를 처리할 때까지 영향을 주는 업데이트들 중 지금 버킷이 아닌 것들은 이미 Union-Find 에 삽입되어 있을 것이다. 이제 각 쿼리마다 최대 B개의 업데이트들을 Naive 하게 하나씩 보며 Union-Find 에 삽입한다. 중요한 것은 Union-Find에 삽입한 후에는 다시 원래 상태로 돌려줘야 한다는 것이다. 이는 dynamic connectivty 문제에서 사용한 복구가 되는 union-find 자료구조를 이용하면 해결할 수 있다. 


다음 버킷으로 넘어갈 때에는 계속 정렬된 간선 집합을 갖고 있어야 한다. 이는 merge sort 와 비슷하게 두개의 정렬된 집합들을 합쳐주면 된다.


시간 제한이 촉박한 문제이기 때문에 버킷의 크기 상수를 잘 잡아 주어야 하는데, sqrtN 인 300정도로 잡으면 TLE가 나오지만 버킷 사이즈를 조금 더 키우는 대신 전체 연산의 횟수를 줄이기 위하여 800정도로 AC를 받았다. 


시간 복잡도 : O(QsqrtNlogN + NsqrtNlogN)



'APIO' 카테고리의 다른 글

APIO 2016  (0) 2019.09.13
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함