백준 1644번, 소수의 연속합 풀이
2020. 12. 21. 22:46ㆍProblem Solving/백준
문제
풀이
에라토스테네스 체를 사용하여 구간 $[2, N]$ 사이의 모든 소수를 구해줍니다.
구한 소수를 배열에 오름차순으로 두고, 그 배열의 길이를 $ K $로 둡니다.
어떤 구간이 $ N $이 되는지 찾아봅시다.
첫번째로는 누적합을 사용하여 모든 $ i, j (1 \leq i \leq j \leq N) $에 방문하여 $ psum[j] - psum[i] = N$인지 확인해주면 $ O(K^2) $에 가능합니다.
하지만, $ N $의 범위가 $ 4,000,000 $이므로 $ K $가 $ 10,000 $을 넘어가서 제곱 풀이는 사용할 수 없습니다.
누적합에서는 모든 $i, j$를 방문하지만, 투포인터로 가능한 $i, j$만 빠르게 탐색할 수 있습니다.
투포인터의 왼쪽 포인터를 $ L $, 오른쪽 포인터를 $ R $이라고 두고, 구간 $ [L, R] $의 합을 $sum$이라고 둡니다.
매번,
$ sum \leq N $이면, $ sum $에 $ prime[R] $을 더해주고, $ R $을 1 증가시킵니다.
그렇지 않다면, $ sum $에 $ prime[L] $을 빼주고, $ L $을 1 증가시킵니다.
최악의 경우, $ L, R $이 모두 $ 1...K $를 방문하므로 기존의 시간복잡도(누적합 완탐) $ O(K^2) $에서 $ O(2K) $로 줄일 수 있고, 상수가 무시할만한 수준이므로 복잡도는 $ O(K) $가 됩니다.
소스코드
#include <bits/stdc++.h>
#define all(v) v.begin(), v.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair <ll, ll> pll;
typedef vector<int> vi;
typedef vector <ll> vl;
vi vis(4000001);
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
vi prime;
int n; cin >> n;
int k = n;
for (int i = 2; i <= k; i++) {
if (!vis[i]) prime.push_back(i);
for (int j = i; j <= k; j += i) vis[j] = 1;
}
if (!prime.size()) {
cout << "0";
return 0;
}
int l = 0, r = 1, sum = prime[0], ans = 0;
for (; l < prime.size(); ) {
if (n == sum) ans++;
if (n <= sum) {
sum -= prime[l++];
} else {
if (r < prime.size()) sum += prime[r++];
else break;
}
}
cout << ans;
}
'Problem Solving > 백준' 카테고리의 다른 글
백준 14621번, 나만 안되는 연애 풀이 (0) | 2020.12.25 |
---|---|
백준 15918번, 랭퍼든 수열쟁이야!! 풀이 (0) | 2020.12.22 |
백준 20312번, CPU 벤치마킹 풀이 (0) | 2020.12.11 |
백준 5052번, 전화번호 목록 (0) | 2020.12.08 |
백준 5578번, JOI 2009 예선 4번, 薄氷渡り 풀이 (0) | 2020.11.30 |