프로그래밍/알고리즘

[알고리즘] Codeforces Round #768 (Div. 2)

riroan 2022. 1. 29. 18:21

코드포스에 너무 어려움을 느껴 앞으로 editorial를 보고 공부하여 블로그에 정리하고자 한다..

(Div2 2솔까지는 할만한데 3솔부터 못해먹겠다.)

 

A - min max swap [Solved!]

두 배열 a1,a2,...,ana1,a2,...,anb1,b2,...,bnb1,b2,...,bn이 주어졌을 때 같은 인덱스들을 여러번 적절히 바꾸어 만들어진 새로운 두 배열을 만든다. 만들어진 각 배열의 최댓값을 곱했을 때 가능한 최솟값을 구하는 문제이다.

 

ai,biai,bi를 비교하면서 두 수중 작은 값을 aiai, 큰 값을 bibi에 넣으면 최적해가 구해진다.

 

B - Fun with Even Subarrays [Solved!]

배열이 주어졌을 때 적당한 범위 al,al+1,...,aral,al+1,...,ar을 정해서 k=rl+1k=rl+1이라 할 때 aik=aiaik=ai라 하자.(lirlir) 이 연산을 유한번 반복하면 모든 배열의 원소가 같아지는데 그 횟수의 최솟값을 구하는 문제이다.

 

배열은 앞에 있는 값을 뒤에 있는 값으로 대체하는 문제이므로 맨 뒤에 있는 값은 변할 수 없다. 결국 최종 배열은 맨 뒤에 있는 값만 가진 배열이 나올것이다. 그럼 뒤에서부터 같은 값이 얼마나 연속해서 있는지만 구하면 된다.

예를 들어 배열이 [4,2,1,3,3][4,2,1,3,3]이라고 하자.

마지막 값은 3이고 연속해서 2개가 있다.

이를 연산 처리하면 [4,3,3,3,3][4,3,3,3,3]이 된다.

같은 방법을 한번 더 사용하면 마지막 값이 연속해서 4개가 있으므로 [3,3,3,3,3][3,3,3,3,3]이 되어 2번만에 배열의 값을 같게 만들 수 있다.

 

C - And Matching

nn은 2의 제곱수이고 (0,1,...,n1)(0,1,...,n1)인 배열이 있을 때 이를 적당히 두개씩 짝지으면 n2n2개의 집합이 만들어진다. ((a1,b1),(a2,b2),...,(an2,bn2))((a1,b1),(a2,b2),...,(an2,bn2)) 이때 n2i=1ai&bi=kn2i=1ai&bi=k를 만들 수 있는지 묻는 문제이다.

 

내가 몰랐던 비트마스킹 기법을 사용한다. 우선 2=1022=102이고 ~2=0122=012인데 4자리 not연산을 하려면 215=1101(2)215=1101(2)가 된다. 이를 일반화하면 aa가 있고 nn자리 not연산을 하려면 a(2n1)a(2n1)을 하면 된다. (2n1=11...122n1=11...12이기때문)

또한 a&a&~a=0a=0임은 자명하다. (근데 난 이것도 몰랐다. 이산수학을 들어야 하는 이유)

 

* Case 1) k=0k=0

n2x=0x&n2x=0x&~xx로 구성하면 00이 나온다.

 

* Case 2) 0<k<n10<k<n1

k&(n1)=kk&(n1)=k 이고 ~k&0=0k&0=0이니까 남은 수 0<x<n2,xk,0<x<n2,xk,~kk에 대해 (x,(x,~x)x)로 구성하면 kk 가 나온다.

 

* Case 3) k=n1k=n1

이것도 2와 비슷하게 하면 되는데 n1&n1n1&n1이 안되기 때문에 n1&n2n1&n2를 하고 11을 찾자. 아무 홀수나 잡아도 되지만 가장 무난한 n3n3을 고르면 n3&1=1n3&1=1이 나온다. 그 후 (0,2)(0,2)을 구성하고 남은 수들에 대해 (x,(x,~x)x)로 짝지으면 된다.

여기서 n=4n=4일때는 n3&1n3&1을 할 수 없으므로 해가 존재하지 않는다.

 

D - Range and Partition

배열이 주어지고 그 배열을 다음 조건에 맞게 정확히 kk개의 부분배열을 만든다.

- 각 부분배열은 원래 배열에서 연속한 일부분이어야 한다.

- 원래 배열의 각 원소는 부분 배열중 하나에만 존재한다. (분할한다는 뜻인듯)

- 각 부분배열에서 [x,y][x,y]구간 안에 있는 원소 수가 구간 밖의 원소 수보다 크다.

이때 yxyx를 최소화하는 방법을 구하는 것이 문제이다.

 

굉장히 기발한 방법으로 해결한다.

우선 구간 [x,y][x,y]를 정하고 새로운 배열 bb를 정의하는데 xaiyxaiy 이면 bi=1bi=1이고 그 외에는 bi=1bi=1로 정의한다. (이런 발상이 놀라웠다.)

그리고 bb에 대한 부분합 psumpsum을 구한다.

만약 전체 합 psumnkpsumnk라면 조건에 만족하는 kk개의 배열을 만들 수 있으므로 1<x<k1<x<k에 대해 처음으로 psumi=xpsumi=x일 때마다 분할하면 된다. 예를들어 psum=[1,2,1,2,3,4],k=3psum=[1,2,1,2,3,4],k=3일 때 [1],[2],[1,2,3,4][1],[2],[1,2,3,4]로 분할하면 정답이 된다.

정답을 구하기 위해 구간 [x,y][x,y]를 구하는 과정은 다음과 같다.

psumn=ni=1bi=ni=1(1+2[xaiy])kpsumn=ni=1bi=ni=1(1+2[xaiy])k

ni=1[xaiy]k+n2ni=1[xaiy]k+n2

등호일 때 최소가 되고 원래 배열을 정렬을 해서 AA배열을 얻었을 때 Ai+k+n21AiAi+k+n21Ai의 최솟값을 구하면 된다.

 

E - Paint the Middle

???

 

F - Flipping Range

???

 

코드포스는 배열을 적당히 조작하는걸 좋아하는것 같다.