본문 바로가기

카테고리 없음

ICPC P) Halfway

 

문제 설명

1, 2, 3, 4, ...., n 까지 숫자를 나열할 수 있다. 

연속한 숫자들끼리 연결할 수 있는데 한번 연결하면 끝이고 다시 연결할 수 없다. 게다가 순서도 1 -> 2 -> 3 -> ... 순서대로 연결함

그래서 예를 들어 n이 5라면 

1 - 2, 3, 4

2 - 3, 4

3 -4 

이렇게 연결할 수 밖에 없다. 

 

그래서 문제는 이런 연결들의 개수 중 가장 중간에 있는 연결이 어떤 것인지 측정하는 것이다.

5를 예로 들면 연결은 총 3 + 2 + 1해서 6개가 되는데 그 중 중간 값인 3이 1 - 4이므로 1이 답이 된다. 

 

생각할 점

하.... 또 입력 범위가 10^9이다.

이분탐색으로 풀어야 한다.

 

---

이분탐색! 을 써야 겠다는 이해했는데 범위랑 n이 너무 헷갈렸다..

 

n이 6이라고 가정하면 1, 2, 3, 4, 5 이렇게 숫자가 있을거고 (연결하지 못하는 6은 제외하자)

각 연결은

1 - 2, 3, 4, 5, 6

2 - 3, 4, 5, 6

3 - 4, 5, 6

4 - 5, 6

5 - 6

 

이렇게 되어 있을 것이다. 연결 개수를 보면 

1 - 5

2 - 4

3 - 3

4 - 2

5 - 1

이렇게 되어 있는데 이걸 쭉 나열해보자

 

1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5

 

여기서 l, r, mid 값을 어떻게 둘지 너무 어려웠다.. ㅠ 지금은 다 풀었으니 알겠는데 처음 볼 때 이거 값을 어떻게 두지 계속 고민했음 악악

 

암튼,,, 1, 2, 3, 4, 5 중 하나를 선택해서 그 숫자의 범위가 중간 값인지 계산해야 한다. 

1, 1, 1, ... 이 숫자들의 합이 15라서 중간값인 7이 어느 범위에 있는지 확인해야 함.

 

먼저 l을 1 r을 5로 잡자. 여기서 중간 값이 3이라 3의 범위에 구하는 수 7이 있는지 확인한다.

3의 범위는 10, 11, 12라서 (하... 범위 구하는 것도 어려웠다) 7은 여기에 속해있지 않다.

그럼 이제 범위를 [1, 2]로 옮긴다 그럼 중간값은? 1임 1의 범위는 1, 5이다. 그러면 7은? 또 안 속함

그럼 범위는 정해졌다. 2임 따란

 

후... 이렇게 구했다... 이분탐색 어려워함 + 범위 어떻게 잡을지 모름 = 와장창...

그래도 뭐... 풀었으니깐... 성장했겠지. 열심히 하자는 마음으로 정리라도 해본다.

 

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
#include <string>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cmath>
#include <cstring>
#include <bitset>
 
#define xx first
#define yy second
#define all(x) (x).begin(), (x).end()
 
using namespace std;
using i64 = long long int;
using ii = pair<int, int>;
using iis = pair<int, string>;
using ii64 = pair<i64, i64>;
using iii = tuple<int, int, int>;
 
int main() {
    i64 n;
    scanf("%lld", &n);
    
    i64 l = 1;
    i64 r = n - 1;
    i64 calc = (n * (n - 1) / 2 + 1) / 2;
    // printf("calc : %d\n", calc);
    
    while (l <= r) {
        i64 mid = (l + r) / 2;
        
        i64 start = (mid - 1) * n  - (mid - 1) * mid / 2 + 1;
        i64 end = start + (n - mid) - 1;
        
        // printf("l : %d r : %d mid :%d start: %d, end: %d\n", l, r, mid, start, end);
        
        if (calc < start)
            r = mid;
        else if (end < calc)
            l = mid;
        else {
            printf("%lld\n", mid);
            return 0;
        }
    }
    printf("%lld\n", l);
    
    return 0;
}

범위는 적당히 규칙 보고 찾았다.

 

 

아 처음에 중간값 구할 때 실수한 게 있다. 그냥 전체 합에서 절반이라 생각해 n * (n - 1) / 4라고 했는데 이러면 3을 넣으면 2가 나와야 하는데 1이 나온다. 제출하니 애매하게 46에서 틀렸는데 이게 원인이었다. 

 

 

i64 calc = (n * (n - 1) / 2 + 1) / 2;

 

왜 이렇게 바꿨는지 이해도 못하고 일단 썼지만 다시 한번 생각해보았다.

아하

이게 1부터 시작해서 숫자 범위가 늘어났다. 그래서 내림을 해주면 안 되고 올림을 해줘야 했다. 그렇구나... 신경써줘야할게 왜이리 많지 헝..

그래도 끝냈다. 휴