반응형
문제 요약
처음에 $a_i = i$인 크기가 N인 배열 $a$가 주어진다. 이 배열에 대해서 다음과 같은 쿼리를 수행하고 모든 쿼리를 수행한 뒤에 배열 $a$를 출력한다.
풀이
해당 문제는 스플레이 트리로 해결이 가능하다. 제일 처음에는 선형 모양의 스플레이 트리를 만들고 1부터 N까지를 차례대로 값에 넣어주도록 하자.
1번 쿼리는 각 노드 별로 최솟값, 최댓값, 합을 관리해줌으로 값은 구할 수 있고 구간을 뒤집는 연산은 Lazy Propagation으로 가능하다.
2번 쿼리는 k가 양수인 경우에는 [l,r]을 오른쪽으로 shift해야 되는데 이 경우는 구간을 뒤집는 연산을 세번 하면 가능하다.
[r-k+1, r]을 먼저 뒤집고, [l,r-k]를 뒤집고 그 다음엔 [l,r]을 뒤집으면 shift가 완료된다.
왼쪽으로 할 경우에는 [l, l+k-1]을 뒤집고, [l+k,r]을 뒤집고, [l, r]을 뒤집으면 완료된다.
3번 쿼리는 i번째 원소를 찾고 그 노드를 스플레이 해주면 된다.
4번 쿼리는 처음에 트리를 선형으로 만들어줄 때, i가 있는 노드의 포인터를 저장해주자. 그 포인터를 $p_i$라고 하면 이 노드를 스플레이 해주면 인덱스는 루트의 서브트리 크기 사이즈를 통해서 알 수 있다.
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
struct Node {
Node *left, *right, *p;
ll sz;
ll val, mi, mx, sum;
bool inv;
Node() : left(nullptr), right(nullptr), p(nullptr) {};
} *root;
Node *indices[300005];
void push(Node *cur) {
if(!cur->inv) return ;
swap(cur->left, cur->right);
if(cur->left) cur->left->inv = !cur->left->inv;
if(cur->right) cur->right->inv = !cur->right->inv;
cur->inv = false;
}
void update(Node *cur) {
cur->sz = 1;
cur->mi = cur->mx = cur->sum = cur->val;
if(cur->left) {
cur->sz += cur->left->sz;
cur->sum += cur->left->sum;
cur->mi = min(cur->mi, cur->left->mi);
cur->mx = max(cur->mx, cur->left->mx);
}
if(cur->right) {
cur->sz += cur->right->sz;
cur->sum += cur->right->sum;
cur->mi = min(cur->mi, cur->right->mi);
cur->mx = max(cur->mx, cur->right->mx);
}
}
void rotate(Node *cur) {
Node *p, *b;
p = cur->p;
if(!p) return ;
push(p); push(cur);
Node *pp = p->p;
if(p->left == cur) {
p->left = b = cur->right;
cur->right = p;
}
else {
p->right = b = cur->left;
cur->left = p;
}
p->p = cur;
cur->p = pp;
if(b) b->p = p;
if(!pp) root = cur;
else {
if(pp->left == p) pp->left = cur;
else pp->right = cur;
}
update(p); update(cur);
}
void splay(Node *cur) {
while(cur->p) {
Node *p = cur->p;
Node *pp = p->p;
if(pp) {
if((pp->left == p) == (p->left == cur)) rotate(p);
else rotate(cur);
}
rotate(cur);
}
}
void findKth(ll k) {
Node *p = root;
push(p);
while(1) {
while(p->left && p->left->sz > k) {
p = p->left;
push(p);
}
if(p->left) k -= p->left->sz;
if(!k) break;
else --k;
p = p->right;
push(p);
}
splay(p);
}
void segment(int l, int r) {
findKth(l-1);
Node *p = root;
root = p->right;
root->p = nullptr;
findKth(r-l+1);
root->p = p;
p->right = root;
root = p;
}
int N, Q;
void init() {
Node *p;
root = p = new Node;
p->val = 0;
p->inv = false;
indices[0] = p;
for(int i=1;i<=N;++i) {
p->right = new Node;
p->right->p = p;
p = p->right;
p->val = i;
p->inv = false;
indices[i] = p;
}
p->right = new Node;
p->right->p = p;
p = p->right;
p->val = 0;
p->inv = false;
indices[N+1] = p;
while(p) {
update(p); p = p->p;
}
}
void flip(int l, int r) {
segment(l,r);
Node *p = root->right->left;
p->inv = !p->inv;
}
void inorder(Node *cur) {
push(cur);
if(cur->left) inorder(cur->left);
if(cur->val) cout << cur->val << ' ';
if(cur->right) inorder(cur->right);
}
int main() {
cin.tie(nullptr); ios::sync_with_stdio(false);
cin >> N >> Q;
init();
for(int i=0;i<Q;++i) {
int a,b,c,x;
cin >> a;
if(a == 1) {
cin >> b >> c;
segment(b,c);
Node *p = root->right->left;
cout << p->mi << ' ' << p->mx << ' ' << p->sum << '\n';
p->inv = !p->inv;
}
else if(a == 2) {
cin >> b >> c >> x;
segment(b,c);
Node *p = root->right->left;
cout << p->mi << ' ' << p->mx << ' ' << p->sum << '\n';
if(b == c) continue;
int k = c-b+1;
if(x > 0) {
x %= k;
if(x == 0) continue ;
flip(c-x+1,c);
flip(b,c-x);
flip(b,c);
}
else if (x < 0) {
x = -x;
x %= k;
if(x == 0) continue ;
flip(b,b+x-1);
flip(b+x,c);
flip(b,c);
}
}
else if(a == 3) {
cin >> b;
findKth(b); cout << root->val << '\n';
}
else {
cin >> b;
splay(indices[b]); cout << root->left->sz << '\n';
}
}
inorder(root);
cout << '\n';
return 0;
}
반응형
'Problem Solving > 문제풀이' 카테고리의 다른 글
백준 13543번 수열과 쿼리 2 (0) | 2021.02.17 |
---|---|
백준 3444번 Robotic Sort (0) | 2021.02.17 |
백준 16977번 히스토그램에서 가장 큰 직사각형과 쿼리 (0) | 2021.02.17 |
백준 10076번 휴가 (1) | 2021.02.17 |
백준 17373번 녜힁 (0) | 2021.02.17 |