31786번: 최고의 친구
boj.ma
2가지 쿼리가 주어집니다.
1번 쿼리 : $(x, 0), (-x, 0)$ 에 위치할때 통신 가능한 기지국의 개수 출력
2번 쿼리 : $(x, y)$ 에 기지국이 있었으면 삭제, 없었으면 추가
$(x, 0)$ 와 $(-x, 0)$ 이 통신하기 위해서는 어떤 기지국 $(a, b)$ 에 대해 세 점이 이루는 삼각형이 예각 삼각형이어야 합니다.
1번 쿼리를 보고 예각 삼각형이 되어야 하므로 당연히 점 $(a, b)$ 의 a가 $ -x < a < x $ 여야 한다는 건 알았습니다.
처음엔 저 사이에만 있으면 무조건 예각삼각형이라고 착각했습니다.
중3때 배우는 기하학을 갖고와봅시다
반원에 대한 원주각은 항상 직각입니다.
$(x, 0)$ 와 $(-x, 0)$ 을 지름으로 하는, 반지름의 길이가 x이고 중심이 $(0, 0)$인 원을 생각하면
원안에 있는 모든 점들에 대해서 만들어지는 삼각형은 절대 예각삼각형이 될 수 없습니다.
따라서 임의의 기지국 좌표 $(a, b)$에 대해 $ -x < a < x $ 를 만족하면서 $ a^2 + b^2 > x^2$ 인 점들만 답이 될 수 있습니다.
그럼 $ -x < a < x $ 의 개수에서 $ a^2 + b^2 <= x^2$ 인 점의 개수를 빼주면 1번 쿼리를 처리할 수 있습니다.
쿼리의 개수가 $3*10^5$개 주어지는 좌표의 범위가 $[-10^9, 10^9]$ 이므로 당연히 완전탐색으로 쿼리를 처리할 수 없습니다.
1번쿼리는 특정 범위인 값의 개수, 2번쿼리는 특정 좌표를 추가하거나 삭제하는 쿼리이므로
세그먼트 트리를 생각할 수 있습니다.
그런데, 좌표의 범위가 크기때문에 좌표압축을 해줘야할 것 같은데 온라인으로 좌표가 주어지기때문에 힘들어 보입니다.
그래서 쿼리를 미리 다 받아서 나올 수 있는 모든 $x$좌표와 $x^2+y^2$의 값을 미리 계산하여 좌표압축 후에
세그먼트 트리를 적용하면 됩니다.
여기까지 생각하고 세그먼트 트리를 통해 구현을 하다가 pbds를 통해서도 충분히 위의 쿼리를 처리할 수 있다고 판단하여 pbds로 구현했습니다.
그리고, $x^2 + y^2$의 값들을 관리할때 좌표가 다르더라도 $x^2 + y^2$의 결과는 같을 수 있습니다.
따라서 기지국의 좌표를 따로 관리하는 자료구조(set, map..)도 필요합니다.
구현(c++)
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define endl "\n"
#define NM 101010
#define MAX 1010100
#define BIAS 1048576
#define MOD 1000000007
#define X first
#define Y second
#define INF 100000000
#define FOR(i) for(int _=0;_<(i);_++)
#define pii pair<int, int>
#define pll pair<ll, ll>
#define all(v) v.begin(), v.end()
#define fastio ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 i128;
using namespace std;
//pbds -- order_of_key(x) := x미만의 원소의 개수(x가 들어가야할 인덱스 0-based) , find_by_order(k) := (k + 1)번째 원소가 있는 iter
using namespace __gnu_pbds;
// multi-set
using ordered_set_equal = tree<ll, null_type, less_equal<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using ordered_set_equal_pll = tree<pll, null_type, less_equal<pll>, rb_tree_tag, tree_order_statistics_node_update>;
// set
using ordered_set = tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>;
int main() {
fastio
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ordered_set_equal x_, xy_;
ordered_set_equal_pll xy_cd;
// multi-set 원소삭제
auto pbds_erase = [&](ordered_set_equal &a, ll val)->void{
auto it = a.find_by_order(a.order_of_key(val));
if(*it == val) a.erase(it);
};
auto pbds_erase_pll = [&](ordered_set_equal_pll &a, pll val) -> void{
auto it = a.find_by_order(a.order_of_key(val));
if (*it == val) a.erase(it);
};
int q;
cin >> q;
while(q--){
int oper;
cin >> oper;
if (oper&1){
ll x;
cin >> x;
// (-x, x)구간의 갯수
int x_cnt = x_.order_of_key(x) - x_.order_of_key(-x + 1);
// a^2 + b^2 <= x^2인 (a, b)의 갯수
int xy_cnt = xy_.order_of_key(x * x + 1);
cout << x_cnt - xy_cnt<< endl;
}else{
ll x, y;
cin >> x >> y;
// x^2 + y^2
ll cur = x * x + y * y;
auto it = xy_cd.find_by_order(xy_cd.order_of_key({x, y}));
// 이미 존재하는 기지국
if (it->first == x && it->second == y) {
pbds_erase(xy_, cur);
pbds_erase(x_, x);
pbds_erase_pll(xy_cd, make_pair(x, y));
}else{
xy_.insert(cur);
x_.insert(x);
xy_cd.insert(make_pair(x, y));
}
}
}
return 0;
}
기지국의 좌표를 관리하는건 굳이 pbds를 통해 관리하지 않아도 됩니다.
'알고리즘 문제 풀이' 카테고리의 다른 글
백준 7346 유전자 함수 (0) | 2024.05.16 |
---|---|
백준 23889 돌 굴러가유 (0) | 2024.05.16 |
백준 31782 저체온증 (0) | 2024.05.02 |
백준 31778 PPC 만들기 (1) | 2024.05.02 |
백준 10719 Baekjoon Database Management System (0) | 2024.04.30 |