안녕하세요. 오늘은 어려운 연속합 문제를 풀어볼 거에요.
https://www.acmicpc.net/problem/16977
각각의 문제에 대해서는 이분탐색을 통한 결정문제로 해결할 수 있습니다.
높이의 최솟값이 x일때 가능한가?
이는 높이가 x이상인 직사각형들만 남겨놓고 구간 [l,r]에서 1만 있는 연속합의 최댓값을 가져오면 할 수 있습니다.
하지만 문제는 이러한 쿼리들이 너무 많다는 것입니다.
이때는 병렬 이분탐색을 쓰면 됩니다.
일단 값들이 크기 때문에 값 압축을 합시다.
nums[i]는 i번째 작은 수이고 인덱스는 0부터 시작, 중복은 제거되어있습니다.
num_arr[x]는 값이 x인 수들의 인덱스들을 모두 모아놓은 벡터입니다.
qry에는 모든 쿼리의 값들이 들어가 있습니다. 인덱스는 1부터 시작입니다.
그다음 PBS를 해주면 됩니다.
#include <iostream>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
struct Tree {
ll left, right, ans, all;
};
Tree tree[404040] = { {0,0,0,0} };
void init(ll s, ll e, ll node)
{
tree[node] = { 0,0,0,0 };
if (s == e) return;
ll mid = (s + e) / 2;
init(s, mid, node * 2);
init(mid + 1, e, node * 2 + 1);
return;
}
Tree Union(Tree A, ll s1, ll e1, Tree B, ll s2, ll e2)
{
Tree res;
res.all = A.all + B.all;
res.left = A.left;
res.right = B.right;
res.ans = max({ A.ans,B.ans ,A.right + B.left });
if (A.all == e1 - s1 + 1) res.left = max(res.left, A.all + B.left);
if (B.all == e2 - s2 + 1) res.right = max(res.right, B.all + A.right);
return res;
}
Tree Query(ll s, ll e, ll node, ll l, ll r)
{
if (e < l || r < s) return { 0,0,0,0 };
if (l <= s && e <= r) return tree[node];
ll mid = (s + e) / 2;
Tree first = Query(s, mid, node * 2, l, r), second = Query(mid + 1, e, node * 2 + 1, l, r);
return Union(first, s, mid, second, mid + 1, e);
}
void change(ll s, ll e, ll node, ll idx)
{
if (e < idx || idx < s) return;
if (s == e)
{
tree[node] = { 1,1,1,1 };
return;
}
ll mid = (s + e) / 2;
change(s, mid, node * 2, idx);
change(mid + 1, e, node * 2 + 1, idx);
tree[node] = Union(tree[node * 2], s, mid, tree[node * 2 + 1], mid + 1, e);
}
ll Left[101010] = { 0 }, Right[101010] = { 0 }, Ans[101010] = { 0 };
vector <ll> mid[101010];
void mid_init()
{
for (ll i = 0; i <= 100000; i++) mid[i].clear();
}
vector <ll> nums, num_arr[101010];
int main()
{
ios_base::sync_with_stdio(false); cin.tie(NULL);
ll N, i, x, a, b, c, Q;
vector <pair <ll, ll> > arr;
vector <pair <pair <ll, ll>, ll> > qry;
cin >> N;
arr.push_back({ 0,0 }); //인덱스는 1부터
for (i = 1; i <= N; i++)
{
cin >> a;
arr.push_back({ a,i });
nums.push_back(a);
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
ll numN = nums.size();
for (i = 1; i <= N; i++)
num_arr[lower_bound(nums.begin(), nums.end(), arr[i].first) - nums.begin()].push_back(i);
sort(nums.begin(), nums.end(), greater<>());
cin >> Q;
qry.push_back({ {0,0},0 }); //인덱스는 1부터
for (i = 1; i <= Q; i++)
{
cin >> a >> b >> c;
qry.push_back({ {a,b},c });
Left[i] = 0;
Right[i] = numN - 1;
}
while (true) //PBS
{
mid_init();
bool done = true;
for (i = 1; i <= Q; i++)
{
if (Left[i] < Right[i])
{
done = false;
mid[(Left[i] + Right[i]) / 2].push_back(i);
}
}
if (done) break;
init(1, N, 1);
for (i = 0; i < numN; i++) //i번째 큰 직사각형까지 있음
{
for (ll j : num_arr[numN - i - 1]) change(1, N, 1, j);
for (ll idx : mid[i])
{
if (Query(1, N, 1, qry[idx].first.first, qry[idx].first.second).ans >= qry[idx].second)
{
Ans[idx] = nums[i];
Right[idx] = i;
}
else Left[idx] = i + 1;
}
}
}
for (i = 1; i <= Q; i++)
{
if (Ans[i]) cout << Ans[i] << "\n";
else cout << nums[Left[i]] << "\n";
}
}
감사합니다.