수학적 관찰이 필요한 문제
문제는 진짜 잘 만들었지만 증명하는 데 꽤 오래 걸렸다
코테에서 이렇게 나오면 시간 내에는 못 풀 거 같다.
완전 탐색
매 요청마다 리그전의 재미를 계산한다.
Q = 200,000, N = 100,1000, l = 1, r = 100,000 인 경우
200,000 * N!로 시간 초과가 발생한다
완전 탐색 최적화
각 후보 디비전을 계산할 때, 공통적으로 계산되는 부분이 있을 것이다
이를 알기 위해 1번 팀부터 N 번 팀까지 리그전을 진행할 때 생기는 재미에 대한 식을 나열해 본 결과 공통되는 식이 있단 것을 알 수 있다.
아래 표는 예제 1을 풀어쓴 결과다.
표를 보면 1 ~ 3 팀까지 리그전을 할 경우 계산 결과에, 1 ~ 2팀끼리 리그전을 한 계산들이 포함된 것을 알 수 있다.
예시를 좀 더 확장해 보자.
1 ~ 4 팀도 마찬가지로, 1 ~ 3 팀이 치룬 리그전을 한 계산들이 포함된 것을 알 수 있다.
하지만, 재미도에 대한 누적합만으로는 쿼리에 대한 답을 구하는 것이 불가능하다.
새로운 기준에 대한 누적합을 통해 쿼리에 대한 답을 구해야 한다 생각했다.
누적 합
예제의 재미도 계산 표를 관찰한 결과, 새로 팀이 포함될 때, 계산식을 다르게 표현할 수 있단 것을 발견했다.
예를 들자면, 기존 1 ~ 2 팀의 리그전에, 3팀이 포함된다면 기존 식을 이렇게 표현할 수 있다.
마찬가지로, 4팀이 포함된다면 다음과 같이 식을 통합할 수 있다.
이왕 한 김에 마지막 팀까지 포함된 결과를 정리하면 다음과 같다.
각 팀의 인기에 대한 누적합에 따라 식이 정리되는 것을 확인했고
여기에 대한 누적합을 구한다면 문제를 해결할 수 있다 생각했다.
정리하면, 사용할 누적합은 2가지다.
- 리그전의 재미에 대한 누적합
- 팀의 인기에 대한 누적합
누적 합 계산
1팀부터 리그전을 치르면서 얻는 재미 계산은 구하기 쉽지만,
중간부터 시작하는 경우에 대해 재미 계산 구하기는 생각보다 까다로웠다.
4 5 팀끼리만 경기를 치르는 경우를 생각해 보자
1 ~ 3팀끼리 리그를 해서 얻는 재미와 1 ~ 3 팀이 4, 5팀과 경기를 해서 얻는 재미를 빼줘야 한다.
계산식은 다음과 같이 정했다.
(1 ~ 5 팀의 리그전 누적 합) - (1 ~ 3 팀의 리그전 누적 합) - (1, 2, 3 팀이 4팀과 경기한 재미 총합)
- (1, 2, 3 팀이 5팀과 경기한 재미 총합)
여기서 1, 2, 3팀 따로 4, 5 팀 따로 묶어서 수식을 다듬으면 다음과 같다.
(1 ~ 5 팀의 리그전 재미 누적 합) - (1 ~ 3 팀의 리그전 누적 합) -
(1 ~ 3팀의 인기 누적 합 * 4 ~5 팀의 인기 누적합)
코드로 표현하면, 전체 리그전 재미를 total, 해당 팀의 인기를 funny라고 할 때 치환하면 다음과 같다.
total[5] - total[3] - (funny[5] - funny[3]) * funny[3]
쿼리에 들어오는 값 l, r로 치환하면
ex) l = 4, r = 5일 때
total[r] - total[l - 1] - (funny[r] - funny[l - 1]) * funny[l - 1]
란 계산식이 나온다.
코드
import sys
input=sys.stdin.readline
N, Q = map(int, input().split())
popular = list(map(int, input().split()))
funny = [0] * N
total = [0] * N
for i in range(N):
p = popular[i]
if i == 0:
funny[i] = p
else:
funny[i] = funny[i - 1] + p
total[i] = total[i - 1] + p * funny[i - 1]
funny = [0] + funny
total = [0] + total
# print(funny)
# print(total)
def main():
for _ in range(Q):
s, e = map(int, input().split())
ans = total[e]
if s == 1:
print(ans)
else:
temp = (funny[e] - funny[s - 1]) * funny[s - 1]
# print(temp)
# print(total[s - 1])
print(ans - temp - total[s - 1])
if __name__ == "__main__":
main()
'알고리즘 > 백준' 카테고리의 다른 글
백준[1863] 스카이라인 (Python3) (0) | 2023.02.03 |
---|---|
백준[1561] 놀이 기구 (Python3) (0) | 2023.01.30 |
백준[17136] 색종이 붙이기 (Python3) (0) | 2023.01.27 |
백준[21278] 호석이 두 마리 치킨 (Python3, bfs) (0) | 2023.01.27 |
백준[1941] 소문난 칠공주 (Python3) (0) | 2022.12.17 |