F - Double Sum
AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.
atcoder.jp
문제는 매우 간단하다
A1,A2,A3,...,AN이 주어지면, n∑i=1n∑j=i+1max(Aj−Ai,0)을 구하는 문제
n제한이 40만이라 단순하게 풀면 당연히 시간초과...
1. max(a,b) = (|a-b| + a+b)/2
방법은 많이 있던데 아주 간단하고 경이로운 솔루션이 있어서 복기해본다
배열 A에 대한 함수 f를 다음과 같이 정의한다.
f(A)=n∑i=1n∑j=i+1(Aj−Ai)
식을 정리하면
f(A)=n∑i=1(n∑j=i+1Aj−n∑j=i+1Ai)
Ai는 j에 depend하지 않으므로
f(A)=n∑i=1(n∑j=i+1Aj−(n−i)Ai)
∑nj=i+1Aj=prefix[n]−prefix[i]와 같다.
여기서 prefix[i] = A[1] + A[2] + ... + A[i] 누적합
따라서
f(A)=n∑i=1(prefix[n]−prefix[i]−(n−i)Ai)
그러므로 prefix를 먼저 구해놓는다면, 함수 f(A)는 O(N)에 구할 수 있다.
여기서
max(Aj−Ai,0)=|Aj−Ai|+Aj−Ai2
따라서 다음 식을 구해서 2로 나누면 된다.
n∑i=1n∑j=i+1|Aj−Ai|+n∑i=1n∑j=i+1(Aj−Ai)
우항은 f(A)와 같은데 좌항은?
배열 A내에서 서로 다른 두 항 Aj와 Ai의 차이를 양수로 바꾸고 누적한 합인데,
Ai - Aj나 Aj - Ai나 절댓값은 같고 부호만 다르므로,
A를 오름차순 정렬하면 뒤쪽에 있는 값에서 앞쪽에 있는 값을 빼면 항상 양수가 되므로, A를 정렬할 때 A'이라고 한다면,
f(A')과 같다.
def f(A):
n = len(A)
prefix = [0]
for i in range(n):
prefix.append(prefix[-1] + A[i])
v = 0
for i in range(n):
v += (prefix[n] - prefix[i] - (n-i)*A[i])
return v
n = int(input())
A = list(map(int,input().split()))
B = sorted(A)
print((f(B) + f(A))//2)
2. max(a-b,0) = max(a,b) - b
더 간단한 방법이 있다는 것 같은데
max(Aj−Ai,0)=max(Aj,Ai)−Ai이다.
그러므로, 구하고자 하는 합은
n∑i=1n∑j=i+1max(Aj,Ai)−Ai=n∑i=1n∑j=i+1max(Aj,Ai)−n∑i=1(n−i)Ai
여기서 좌항 ∑ni=1∑nj=i+1max(Aj,Ai)는, 모든 i,j에 대하여 Aj,Ai중 최댓값을 의미하므로,
A를 정렬해도 그 값은 바뀌지 않는다.
따라서, A를 오름차순 정렬하면, 항상 Aj가 더 크므로,
n∑i=1n∑j=i+1max(Aj,Ai)=n∑i=1n∑j=i+1Bj
여기서 prefix를 유도해서 ∑nj=i+1Bj=prefix[n]−prefix[i]해도 되긴 하는데..
i = 1부터 n까지, j = i+1부터 n까지는....
i = 1, j = 2,3,4,..,n
i = 2, j = 3,4,...,n
i = 3,j = 4,5,...,n
...
j = 2부터 n까지, i = 1부터 j-1까지와 같다.
j = 2, i = 1
j = 3, i = 1,2
....
따라서 i,j를 뒤바꾸면 Bj는 i에 depend하지 않으므로...
n∑j=2j−1∑i=1Bj=n∑j=2(j−1)Bj
이 값은 A를 정렬하면 O(NlogN)에 찾을 수 있다.
n = int(input())
A = [0] + list(map(int,input().split()))
B = sorted(A)
v1 = 0
for j in range(2,n+1):
v1 += (j-1 * B[j])
v2 = 0
for i in range(1,n+1):
v2 += ((n-i) * A[i])
print(v1-v2)
'알고리즘 > 애드 혹 알고리즘' 카테고리의 다른 글
정수들이 섞여있는 수열에서 연속하는 정수 수열을 O(N)에 분리하기 (0) | 2024.05.12 |
---|---|
무방향 연결그래프에서 정점의 성질 관찰하기 (0) | 2024.05.10 |
코딩테스트 복기 - 복잡한 그래프 생각하면서 효율적으로 탐색 하기 (0) | 2024.02.03 |
XOR 문제에 접근하기 위해 반드시 필요한 스킬3 -모든 쌍의 XOR의 합(sum of all pair of xor)- (0) | 2024.01.27 |
XOR 문제에 접근하기 위해 반드시 필요한 스킬2 - 연속하는 부분열의 XOR- (0) | 2024.01.25 |