https://www.acmicpc.net/problem/10736
수열의 최대 길이 찾기
XOR의 성질에 따라 V⊕V=0 이므로 XOR 과정 중 중복되는 수를 만나서는 안된다.
다시 말해 어떤 세 원소를 선택하든 최소 한 개의 비트를 살릴 수만 있으면 된다는 것이다.
그렇다면 100 이하의 수로 구성할 수 있는 수열의 최대 길이는 어떻게 찾을 수 있을까?
32와 64를 이진수로 나타낸 표를 보면서 생각해보자.
X | 1 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
이 표에 존재하는 0을 0 혹은 1로 채우는 경우의 수를 만든다고 했을 때 1이 절대로 들어가서는 안 되는 경우가 무엇일까?
X | 1 | 0 | 0 | 0 | 0 | 0 |
1 | NO | 0 | 0 | 0 | 0 | 0 |
바로 64에서 25에 해당하는 비트이다. 만약 이를 허용하면 다음 세 자연수 a(32≤a<64),b(64≤b<96),c(96≤c)에서 비트를 적절히 선택해 XOR의 값을 0으로 만들 수 있게 된다.
저 자리에만 1을 넣지 않으면 나머지 자리는 괜찮다. 앞서 논한 제약조건을 지키며 reading one이 같은 수를 뽑는 경우는 (32)로 결정이 되는데 결국 선택한 수에서 반드시 하나는 reading one을 XOR 연산으로 잃지 않게 되어 언제든 0을 초과한 값을 얻을 수 있다.
따라서 이를 표로 정리하면
X | 1 | YES | YES | YES | YES | YES |
1 | NO | YES | YES | YES | YES | YES |
경우의 수를 계산하면 25+25=64 의 길이를 최대로 가질 수 있다는 결과를 얻을 수 있다.
굳이 64와 32로 결정해야 하나요?
다른 reading one으로 조합을 생각하면 이는 최적해를 가질 수 없음을 이해할 수 있다. reading one이 작을 수록 그만큼 가질 수 있는 비트 조합의 개수가 적어지기 때문이다.
모든 N의 범위에서 수열 길이 찾기
수열의 최대 길이를 찾았으니 이를 N 범위 전체에 적용해보자. 우리는 앞서 가장 큰 reading one과 두번째로 큰 reading one으로 수열을 구성하는 게 최적해임을 알았으니 주어진 N에 대해 N 이하인 가장 큰(그리고 두 번째로 큰) reading one을 가진 수를 찾으면 된다.
이때 reading one이 가장 큰 수에서 reading one의 위치를 x라고 했을 때 구간 [2x−1,2x+(2x−1−1)]에 포함된 수들이 답이 된다. 이를 적절히 구현하면 정답을 받을 수 있게 된다.
전체 코드 - reading one을 찾는 부분은 파라메트릭 서치로 구현했다.
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int reading_bit[7] {1, 2, 4, 8, 16, 32, 64};
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
if (n == 1)
{
cout << "1\n1\n";
continue;
}
if (n == 2)
{
cout << "2\n1 2\n";
continue;
}
int l = 0, r = 6;
int bit_reader;
while (l <= r)
{
int m = (l + r) / 2;
if (reading_bit[m] <= n)
{
bit_reader = m;
l = m + 1;
}
else
{
r = m - 1;
}
}
int dont_access = reading_bit[bit_reader] | reading_bit[bit_reader - 1];
vector<int> res;
for (int i = reading_bit[bit_reader - 1]; i < min(n + 1, dont_access); ++i)
res.push_back(i);
cout << res.size() << '\n';
for (int &i: res)
cout << i << ' ';
cout << '\n';
}
}

'백준 > 수학' 카테고리의 다른 글
백준 16563 - 어려운 소인수분해 (0) | 2021.08.23 |
---|---|
백준 10728 - XOR삼형제 1 (0) | 2021.07.27 |
백준 4375 - 1 (0) | 2021.07.08 |
백준 13172 - Σ (0) | 2021.07.04 |
백준 11444 - 피보나치 수 6 (0) | 2021.06.30 |