문제 설명(링크)
https://www.acmicpc.net/problem/15766
15766번: New Home
Wu-Fu Street is an incredibly straight street that can be described as a one-dimensional number line, and each building’s location on the street can be represented with just one number. Xiao-Ming the Time Traveler knows that there are n stores of k store
www.acmicpc.net
문제 요약
1차원 직선에 여러 가게가 있고 종류는 1부터 $K$이다. 각 가게는 $[a_i, b_i]$시간동안 영업하는데, 쿼리 $Q$개가 들어온다. $y_i$시간에 $l_i$위치에 집을 지을 때, 각 종류의 가게의 접근성은 직선에 존재하는 해당 종류의 가게 중 가장 가까운 것과의 거리이다. 또한 각 집의 후보의 불편도는 각 종류의 가게에 대해 접근성의 최댓값이다. 각 가게 후보에 대해 불편도를 구하라
풀이
일단, 어떤 시점에 위치에 따른 각 종류의 접근성은 아래 그래프처럼 된다.
그러면, 이 종류별 그래프들의 최댓값 그래프(upper envelope)가 위치에 따른 불편도 그래프가 된다. 그러면, 가게가 생겨나고 없어지는 변경 쿼리와 $Q$개의 위치에 대한 답을 물어보는 쿼리를 시간 순으로 정렬해 수행하는 풀이를 생각할 수 있다. 그러면, 저 그래프를 시간에 따라 잘 업데이트하면서 관리하는 방향의 풀이를 생각할 수 있다.
이에는 한 가지의 아이디어가 필요한데, 최댓값 그래프에서 극댓값들을 전부 관리하는 아이디어이다. 이때, 종류별 그래프의 극댓값은 최댓값 그래프에서 사용되지 않을 수도 있다.
저 점들을 모두 안다면, 쿼리를 어떻게 처리할 수 있을까?
일단 $x=l_i$의 직선을 그리고 생각해보자. 직선 왼쪽에 있는 점들은 직선으로 기울기가 -1인 직선(파란색)을 긋고, 직선 오른쪽에 있는 점들은 기울기가 1인 직선(초록색)을 그리자. 그러면 그 직선들과 빨간 직선의 교점들중 가장 높이 있는것이 불편도가 된다. 이를 어떻게 알까? 우선, 한 점에서 이은 파란선과 빨간선의 교점은 x, y좌표의 합과 관련이 있고, 초록선은 차($|x-y|$가 아닌 $x-y$)와 관련이 있다. 따라서 우리는 각 점에 대해 좌표의 합과 차를 저장하고, $x$좌표가 $x_{l_i}$이상의 점들에 대해 좌표의 합의 최대, $x$좌표가 $x_{l_i}$이하의 점들에 대해 좌표의 차의 최대를 관리해주면 된다. 이는 한번의 시뮬레이션을 해 각 시점에 극대점들이 어떻게 추가되고 삭제되는지 전처리하고 쿼리를 해결할 때에는 특정 구간에 있는 유효한(각 시점에서 존재하는) 정점에 대해 좌표의 합과 차의 최대, 최소를 세그먼트 트리로 관리하는 방식으로 풀면 된다.
소스코드
#include <bits/stdc++.h>
#include <cassert>
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("avx,avx2,fma")
using namespace std;
typedef long ll;
typedef pair<ll, ll> pll;
#define MAX 301010
#define MAXS 20
#define INF 1000000000
#define MOD (ll)1000000007
#define bb ' '
#define ln '\n'
template <typename T>
class Segment_Tree {
//0-based index Segment Tree
//O(N), O(lgN)
private:
unsigned int N, s;
vector<T> tree;
vector<unsigned int> l, r;
T query(unsigned int low, unsigned int high, unsigned int loc) {
if (low == l[loc] && high == r[loc]) return tree[loc];
if (high <= r[loc * 2]) return query(low, high, loc * 2);
if (low >= l[loc * 2 + 1]) return query(low, high, loc * 2 + 1);
return query(low, r[loc * 2], loc * 2) + query(l[loc * 2 + 1], high, loc * 2 + 1);
}
void _update(unsigned int loc, T val) {
loc += s;
tree[loc] = val;
loc /= 2;
while (loc) {
tree[loc] = tree[loc * 2] + tree[loc * 2 + 1];
loc /= 2;
}
}
void init(unsigned int x = 1) {
if (x >= s) {
l[x] = r[x] = x - s;
return;
}
init(x * 2);
init(x * 2 + 1);
l[x] = l[x * 2];
r[x] = r[x * 2 + 1];
tree[x] = tree[x * 2] + tree[x * 2 + 1];
}
public:
Segment_Tree<T>() {
}
Segment_Tree<T>(vector<T>& v) {
N = v.size();
s = 1 << (unsigned int)ceil(log2(N));
tree.resize(2 * s + 1);
l.resize(2 * s + 1);
r.resize(2 * s + 1);
unsigned int i;
for (i = 0; i < N; i++) tree[i + s] = v[i];
init();
}
T query(unsigned int low, unsigned int high) { return query(low, high, 1); }
void update(unsigned int location, T new_value) { _update(location, new_value); }
};
struct dat {
ll x, t, a, b;
dat() {}
dat(ll x, ll t, ll a, ll b) :x(x), t(t), a(a), b(b) {}
bool operator<(dat d) {
if (t != d.t) return t < d.t;
if (x != d.x) return x < d.x;
if (a != d.a) return a < d.a;
return b < d.b;
}
};
struct Query {
ll l, y, num;
Query() {}
Query(ll l, ll y, ll num) :l(l), y(y), num(num) {}
bool operator<(Query q) {
return y < q.y;
}
};
map<ll, vector<dat>> arr;
multiset<ll> st[MAX];
vector<Query> query;
ll ans[MAX];
vector<ll> point, tarr;
vector<pll> larr;
ll chk[MAX];
struct node {
ll x, y;
ll lv, rv;
node() :x(0), y(0), lv(0), rv(-INF) {}
node(ll x, ll y) :x(x), y(y) {
lv = x + y;
rv = y - x;
}
node(ll x, ll y, ll lv, ll rv) :x(x), y(y), lv(lv), rv(rv) {}
node operator+(node x) { return node(0, 0, max(lv, x.lv), max(rv, x.rv)); }
};
bool operator<(pll p1, pll p2) {
if (p1.first == p2.first) return p1.second < p2.second;
return p1.first < p2.first;
}
ll getind(pll x) {
return lower_bound(larr.begin(), larr.end(), x) - larr.begin();
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0);
ll N, K, Q;
cin >> N >> K >> Q;
ll i;
ll x, t, a, b;
vector<dat> datset;
for (i = 1; i <= N; i++) {
cin >> x >> t >> a >> b;
arr[a].push_back(dat(x, t, a, b));
arr[b + 1].push_back(dat(x, t, a, b));
tarr.push_back(a);
tarr.push_back(b + 1);
}
for (i = 0; i < Q; i++) {
cin >> a >> b;
query.push_back(Query(a, b, i));
}
sort(query.begin(), query.end());
//simulation
for (i = 1; i <= K; i++) st[i].insert(-INF);
for (i = 1; i <= K; i++) st[i].insert(INF);
map<ll, vector<dat>>::iterator it;
for (it = arr.begin(); it != arr.end(); it++) {
t = it->first;
for (auto d : it->second) {
ll pv = *prev(st[d.t].lower_bound(d.x));
ll ne = *st[d.t].upper_bound(d.x);
if (d.a == t) st[d.t].insert(d.x);
else st[d.t].erase(st[d.t].find(d.x));
larr.emplace_back(pv + ne, d.t);
larr.emplace_back(pv + d.x, d.t);
larr.emplace_back(d.x + ne, d.t);
}
}
Segment_Tree<node> segtree;
vector<node> v(larr.size());
segtree = Segment_Tree<node>(v);
it = arr.begin();
ll cnt = 0;
sort(larr.begin(), larr.end());
larr.erase(unique(larr.begin(), larr.end()), larr.end());
for (i = 0; i < Q; i++) {
Query q = query[i];
while (it != arr.end()) {
ll t = it->first;
if (t > q.y) break;
for (auto d : it->second) {
ll pv = *prev(st[d.t].lower_bound(d.x));
ll ne = *st[d.t].upper_bound(d.x);
if (d.a == t) {
if (st[d.t].find(d.x) == st[d.t].end()) {
segtree.update(getind({ pv + ne, d.t }), node(pv + ne, 0));
segtree.update(getind({ pv + d.x, d.t }), node(pv + d.x, d.x - pv));
segtree.update(getind({ d.x + ne, d.t }), node(d.x + ne, ne - d.x));
}
st[d.t].insert(d.x);
if (!chk[d.t]) cnt++;
chk[d.t]++;
}
else {
st[d.t].erase(st[d.t].find(d.x));
if (chk[d.t] == 1) cnt--;
chk[d.t]--;
if (st[d.t].find(d.x) != st[d.t].end()) continue;
segtree.update(getind({ pv + ne, d.t }), node(pv + ne, ne - pv));
segtree.update(getind({ pv + d.x, d.t }), node(pv + d.x, 0));
segtree.update(getind({ d.x + ne, d.t }), node(d.x + ne, 0));
}
}
it++;
}
if (cnt != K) {
ans[q.num] = -1;
continue;
}
node l = segtree.query(0, getind({ 2 * q.l, 10101010 }) - 1);
node r = segtree.query(getind({ 2 * q.l, -1 }), larr.size() - 1);
ans[q.num] = max(l.lv - 2 * q.l, r.rv + 2 * q.l) / 2;
}
for (i = 0; i < Q; i++) cout << ans[i] << ln;
return 0;
}
'문제풀이' 카테고리의 다른 글
JOI Open Contest 2017 (2) | 2023.01.23 |
---|---|
백준 21794 Navigation 2 (JOISC 2020/2021 Day 4 2번) (0) | 2021.10.05 |
백준 18847. Stray Cat (JOISC 2019/2020 Day 3 3번) (0) | 2021.09.01 |
백준 15261 Donut Drone (CERC 2017 D) (0) | 2021.08.19 |
백준 16977 히스토그램에서 가장 큰 직사각형과 쿼리 (0) | 2021.04.19 |