[백준] 9527 - 1의 개수 세기(Counting ones) (G2)

2022. 2. 13. 00:12·알고리즘 (PS)/BOJ
반응형

https://www.acmicpc.net/problem/9527

 

9527번: 1의 개수 세기

두 자연수 A, B가 주어졌을 때, A ≤ x ≤ B를 만족하는 모든 x에 대해 x를 이진수로 표현했을 때 1의 개수의 합을 구하는 프로그램을 작성하시오. 즉, f(x) = x를 이진수로 표현 했을 때 1의 개수라

www.acmicpc.net

class5 문제를 풀면서 만났던 생 수학 문제.

 

두 정수가 주어질 때, 두 정수를 포함하는 범위의 모든 정수를 이진수로 변환했을 경우

1의 개수를 모두 세는 문제이다.

 

곧이곧대로 구하면 수의 범위가 커서 시간초과를 받는다.

나는 다음과 같은 풀이를 떠올렸다.

 

1. 누적합

[a, b] 구간의 모든 정수의 이진수의 1의 개수를 직접 구하기보다

[1, a] 구간의 이진수의 1의 개수를 구하고

[1, b] 구간의 이진수의 1의 개수를 구한다음

 

[1, b] 구간의 답 - [1, a] 구간의 답 + a의 이진수의 1의 개수

 

를 해서 답을 구하도록 하는 누적합 풀이를 우선 떠올렸다.

 

이걸 코드로 구현하면 아래와 같다.

a, b = map(lambda x: str(bin(int(x)))[2:], input().split())
print(Count(b) - (Count(a) - a.count('1')))

이제 어떻게 [1, x] 구간의 1의 개수를 셀지, 즉, 저 Count 함수에 대한 구현을 고민할 차례다.

 

2. [1, x] 구간의 답을 구하는 방법?

이 부분을 빠르게 계산하기 위해 고민을 했다.

그래서 일단 문제 예시에 나온 2부터 12까지의 수를 전부 노트에 적고,

2진수로 바꾼다음 직접 1의 개수를 세보았다.

 

그렇게 한가지 규칙을 찾을 수 있었다.

 

10110 이라는 이진수의 1부터 모든 1의 개수는

10110 보다 작은 이진수에 대해 1을 채우는 경우의 수를 찾는 것과 같다.

 

10110 의 경우

1010_ 은 반드시 10110 보다 작고

100_ _ 도 반드시 10110보다 작고

0_ _ _ _ 도 반드시 10110 보다 작다

 

(이때, 10_ _ _ 은 고려하지 않는다.

10_ _ _ 은 10111 이라는 10110보다 큰 경우의 수를 가질 수 있기 때문이다.)

 

그리고 1010_, 100_ _, 0_ _ _ _ 은 서로 겹치지 않는다.

따라서 이 각각의 경우에 대해 1의 개수를 세서 합쳐주면 된다.

 

이걸 일반화 하면, 어떤 이진수에 대해

오른쪽에서부터 순회하면서, 1을 만나면 해당 1을 0으로 바꾸고 그 오른쪽의 모든 자리를 빈칸으로 만들었을 때,

해당 빈칸에 값을 채워서 얻을 수 있는 1의 개수의 합이 "해당 1이 0으로 바뀌었을 때의 1의 개수 합" 이다.

이걸 1을 만날 때마다 0으로 바꿔준다고 생각하고 경우의 수를 세서 합치면

우리가 원하는 [1, x] 구간의 1의 개수합을 얻을 수 있다.

 

이때 빈칸의 개수가 n 개이면 해당 n개에 대해 모든 1의 개수의 합은

n 개중 r 개를 1로 채우는 경우의 수에 r 을 곱하는 것을, r에 대해 1부터 n 까지 반복하면 된다.

 

 

보기좋게 수식으로 정리하면 다음과 같다.

PrefixCount 는 100_ _ 처럼 앞에서 고정해둔 부분의 1의 개수이다.

 

이 부분을 코드로 작성한 것이 아래와 같다.

 

def Calculate(n, default_one_count):
    value = default_one_count
    for r in range(1, n+1):
        value += (r + default_one_count) * nCr(n, r)

    return value

여기에서 관건이 바로 nCr 을 빠르게 계산하는 것이다.

제일 큰 수인 10e16 을 이진수로 나타내면 길이가 54자이다.

즉 최악의 경우 54C27 의 경우의 수를 계산해야 하는 것이다.

 

이런 계산을 빠르게 하기 위해 인터넷의 검색으로 조합을 계산하는 함수를 찾았다.

def nCr(n, r):
    if n < 1 or r < 0 or n < r:
        raise ValueError
    r = min(r, n-r)
    numerator = reduce(op.mul, range(n, n-r, -1), 1)
    denominator = reduce(op.mul, range(1, r+1), 1)
    return numerator // denominator
    
    # 출처 : https://brownbears.tistory.com/459

그냥 무식하게 combinations 모듈을 이용해서 리스트화 시키고 길이 구하면 시간이 오래 걸린다.


이걸로 각 구간에 대해 개수도 구하는 방법을 찾았으니 위에서 작성한 코드를 합쳐주면 끝이다.

 

from functools import reduce
import operator as op

def nCr(n, r):
    if n < 1 or r < 0 or n < r:
        raise ValueError
    r = min(r, n-r)
    numerator = reduce(op.mul, range(n, n-r, -1), 1)
    denominator = reduce(op.mul, range(1, r+1), 1)
    return numerator // denominator

def Calculate(n, default_one_count):
    value = default_one_count
    for r in range(1, n+1):
        value += (r + default_one_count) * nCr(n, r)

    return value


def Count(binary):
    whole_one_count = binary.count('1')
    size = len(binary)
    answer = whole_one_count
    for i in range(size):
        if binary[size-1-i] == '0':
            continue
        whole_one_count -= 1
        answer += Calculate(i, whole_one_count)

    return answer

a, b = map(lambda x: str(bin(int(x)))[2:], input().split())
print(Count(b) - (Count(a) - a.count('1')))
반응형
저작자표시 비영리 변경금지 (새창열림)

'알고리즘 (PS) > BOJ' 카테고리의 다른 글

[백준] 16566 - 카드 게임 (P5)  (2) 2022.03.14
[백준] 14244- 트리 만들기 (S4)  (0) 2022.02.14
[백준] 17143 - 낚시왕 (G2)  (0) 2022.02.11
[백준] 1701 - Cubeditor (G2)  (0) 2022.01.31
[백준] 23059- 리그 오브 레게노 (G2)  (0) 2022.01.07
'알고리즘 (PS)/BOJ' 카테고리의 다른 글
  • [백준] 16566 - 카드 게임 (P5)
  • [백준] 14244- 트리 만들기 (S4)
  • [백준] 17143 - 낚시왕 (G2)
  • [백준] 1701 - Cubeditor (G2)
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (614)
      • 개인 프로젝트 (43)
        • 토이 프로젝트 (3)
        • [2020] 카카오톡 봇 (9)
        • [2021] 코드악보 공유APP (22)
        • [2022] 유튜브 뮤직 클론코딩 (9)
        • [2025] 한글 SQL 데이터베이스 (0)
      • 팀 프로젝트 (22)
        • [2020] 인공지능 숫자야구 (4)
        • [2022] OSAM 온라인 해커톤 (10)
        • [2024] GDSC 프로젝트 트랙 (6)
        • [2025] 큰소리 웹 페이지 (2)
      • CS (335)
        • 자료구조 (19)
        • 어셈블리 (41)
        • 멀티미디어응용수학 (7)
        • 컴퓨터 구조 (29)
        • 알고리즘 분석 (4)
        • 컴퓨터 네트워크 (38)
        • 프로그래밍언어론 (15)
        • HCI 윈도우즈프로그래밍 (26)
        • 기초데이터베이스 (29)
        • 운영체제 (23)
        • 오토마타 (24)
        • 문제해결기법 (11)
        • 블록체인 (22)
        • 소프트웨어공학 (21)
        • 기계학습심화 (12)
        • 컴퓨터그래픽스와 메타버스 (8)
        • 분산시스템특론 (6)
      • 자기계발 (44)
        • 생각 정리 (22)
        • 대외활동 (11)
        • 동아리 (7)
        • 자격증 (3)
        • 머니 스터디 (1)
      • 알고리즘 (PS) (107)
        • BOJ (101)
        • Programmers (5)
        • 알고리즘 이모저모 (1)
      • WEB(BE) (8)
        • express.js (1)
        • Spring & Spring Boot (7)
      • WEB(FE) (2)
        • html, css, js (1)
        • React.js (1)
      • Tool & Language (6)
        • Edit Plus (1)
        • Git (1)
        • Python3 (2)
        • Java (2)
      • Infra (12)
        • AWS (1)
        • Oracle Cloud (8)
        • Firebase (2)
        • Network (1)
      • Android (18)
        • Java (6)
        • Flutter (12)
      • Window (2)
        • Visual Studio 없이 WPF (1)
        • MFC (1)
      • 독서 (14)
        • Inside Javascript (7)
        • Database Internals (6)
        • 한 글 후기 (1)
  • 링크

    • github
    • website
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에버듀
[백준] 9527 - 1의 개수 세기(Counting ones) (G2)
상단으로

티스토리툴바