포스트

BOJ 15679 수열의 개수

sorohue가 PS하는 블로그

BOJ 15679 수열의 개수

문제 링크입니다.

문제 요약

각 원소가 31비트 정수로 표현되는 길이 $N$의 수열들 중 정확히 $C$개가 다음의 세 조건을 만족하도록 하는 세 정수 $X, Y, Z$의 조합을 하나 찾으세요.

  • 수열의 모든 원소를 bitwise OR한 결과가 $X$
  • 수열의 모든 원소를 bitwise AND한 결과가 $Y$
  • 수열의 모든 원소를 bitwise XOR한 결과가 $Z$

풀이

문제를 비트 별로 분리해서 생각할 수 있습니다. 수열의 각 원소가 0 또는 1이고, $X,Y,Z$ 또한 0 또는 1이라고 생각하면 총 8가지 조합이 있음을 확인할 수 있습니다. $(X,Y,Z)$에 따라 이를 구분해 보면,

  • $(0,0,0)$ : 모든 원소가 0입니다. 1가지 수열이 이를 만족합니다.
  • $(0, 0, 1), (0,1,0),(0,1,1)$: $X = 0$ 이려면 모든 원소가 0이여야 하므로 불가능합니다.
  • $(1,1,1) / (1,1,0)$ : 모든 원소가 1이여야 합니다. $N$의 홀짝성에 따라 하나는 만족하는 수열이 1가지 존재하고 다른 하나는 불가능합니다.
  • $(1,0,1)/(1,0,0)$ : 이 경우가 수열의 개수를 늘리는 데 핵심적인 역할을 합니다. 원소들이 모두 1도, 모두 0도 아니면서 1의 개수가 홀수/짝수인 경우의 수가 각각의 조합에 배정됩니다. $N$의 홀짝성에 따라 이는 $2^{N-1} -0 \sim 2$ 사이의 값이 됩니다. 이렇게 나뉘는 이유는 이항정리로부터 확인할 수 있습니다.

우리에게는 31개의 비트가 주어졌고, 수열의 개수는 $(1,0,1)$과 $(1,0,0)$의 개수로만 결정됩니다. 나머지는 $(0,0,0)$으로 채워주면서 가능한 모든 경우를 시도해 보면, 실제로 유효한 경우의 수는 $31^2$가지 이하이므로 문제를 해결할 수 있습니다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;

int main(){
	cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
	ll n, c; cin >> n >> c;
	if(!c) return !(cout << "0 0 1");
	if(c == 1) return !(cout << "0 0 0");
	int lgc = 0; ll cc = c+2; while(cc){
		cc >>= 1; lgc++;
	}
	if(n-2 > lgc) return !(cout << -1);
	for(int i = 0; i <= 31; i++) for(int j = 0; i+j <= 31; j++){
		__int128 t = 1;
		bool flag = 1;
		for(int ii = 0; ii < i; ii++){
			t *= (1LL<<n-1)-2+(n&1);
			if(t > c){
				flag = 0;
				break;
			}
		}
		for(int jj = 0; jj < j; jj++){
			t *= (1LL<<n-1)-(n&1);
			if(t > c){
				flag = 0;
				break;
			}
		}
		if(!flag) continue;
		if((ll)t == c){
			ll X = 0, Y = 0, Z = 0;
			for(int ii = 0; ii < i+j; ii++) X |= (1LL<<ii);
			for(int jj = 0; jj < j; jj++) Z |= (1LL<<jj);
			cout << X << ' ' << Y << ' ' << Z << '\n';
			return 0;
		}
	}
	cout << -1;
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.