https://www.acmicpc.net/problem/11437

 

11437번: LCA

첫째 줄에 노드의 개수 N이 주어지고, 다음 N-1개 줄에는 트리 상에서 연결된 두 정점이 주어진다. 그 다음 줄에는 가장 가까운 공통 조상을 알고싶은 쌍의 개수 M이 주어지고, 다음 M개 줄에는 정점 쌍이 주어진다.

www.acmicpc.net


입력을 받은후

1. DFS

입력을 받은뒤, DFS를 돌면서 ( 문제에서 root는 1번노드 ) depth와 부모를 저장해준다.

void dfs(int now, int nowDepth,int prev) {
     visit[now] = true; // visit
     depth[now] = nowDepth; // 현재 노드의 깊이
     par[now][0] = prev; // 현재 노드의 [0]번째 부모( 바로 윗 부모 )
     for (int i = 0; i < vec[now].size(); i++) {
          if (visit[vec[now][i]] == false) {
               dfs(vec[now][i], nowDepth + 1,now);
          }
     }
}

2. 부모의 parser_table 생성

반복문으로 parser_table을 생성한다.

주석으로 자세한 설명을 해놓았다.

   // 2 ^ 19 번째 부모는 입력조건보다 한참 크다, 딱 맞게(타이트하게) 부모의 깊이를 잡으려면
     // tree가 오른쪽으로만 늬여진 일렬로된 트리라고 가정한다면, (k = log(노드갯수)) 에서 par[노드수][k]로 설정하면 된다.
     for (int j = 1; j <= 19; j++) {
          for (int i = 1; i <= N; i++) {
               // i번 노드의 2^j번째 부모 = i의 2^(j-1)번쨰 부모의 2^(j-1) 부모
               // example )
               // par[1][1] = par[par[1][0]][0] : 1번 노드의 (2^1)번째 부모 = 1번노드의 (0)번째 부모의 (0)번째 부모( 즉 부모의 부모 )
               // par[1][2] = par[par[1][1]][1] : 1번 노드의 (2^2)번째 부모 = 1번노드의 (2^1)번쨰 부모의 (2^1)번째 부모 ( 즉, 증조부모의 증조부모 )
               // ...
               par[i][j] = par[ par[i][j - 1]][j - 1];
          }
     }

3. lca 함수

(주석 참고)

int lca(int x, int y) {
     // 깊이가 더 깊은 놈 ( 루트와 거리가 먼놈이 y가 된다 )
     if (depth[x] > depth[y]) SWAP(x, y);

     // y는 현재 더 깊이 있음으로 y를 x의 depth만큼 올려줘야 한다.
     // example)
     // x와 y가 diff가 5만큼 차이 난다고 가정하면
     // y에서 par[y][2] : y의 ( 2^2 ) 번째 부모로 올라감
     // y에서 par[y][0] : y의 ( 1^1 ) 번째 부모로 올라감
     // total 5만큼 높이가 올라가게 된다.
     for (int i = 19; i >= 0; i--) {
          int diff = depth[y] - depth[x];
          if (diff >= (1 << i)) y = par[y][i];
     }
     
     // depth를 맞췄는데 같은 노드번호라면 리턴
     if (x == y) return x;
     
     // 부모가 같을테까지 같은 depth를 똑같이 올려준다
     for (int i = 19; i >= 0; i--) {
          if (par[x][i] != par[y][i]) {
               x = par[x][i];
               y = par[y][i];
          }
     }
     // 같아진 부모의 노드번호를 리턴해준다.
     return par[x][0];
}

전체코드

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

#define MAX_N 100001
#define SWAP(a,b) {int t = b; b = a; a = t;}

vector<int> vec[MAX_N];
bool visit[MAX_N];
int depth[MAX_N];
int par[MAX_N][21];

void dfs(int now, int nowDepth,int prev) {
     visit[now] = true; // visit
     depth[now] = nowDepth; // 현재 노드의 깊이
     par[now][0] = prev; // 현재 노드의 [0]번째 부모( 바로 윗 부모 )
     for (int i = 0; i < vec[now].size(); i++) {
          if (visit[vec[now][i]] == false) {
               dfs(vec[now][i], nowDepth + 1,now);
          }
     }
}

int lca(int x, int y) {
     // 깊이가 더 깊은 놈 ( 루트와 거리가 먼놈이 y가 된다 )
     if (depth[x] > depth[y]) SWAP(x, y);

     // y는 현재 더 깊이 있음으로 y를 x의 depth만큼 올려줘야 한다.
     // example)
     // x와 y가 diff가 5만큼 차이 난다고 가정하면
     // y에서 par[y][2] : y의 ( 2^2 ) 번째 부모로 올라감
     // y에서 par[y][0] : y의 ( 1^1 ) 번째 부모로 올라감
     // total 5만큼 높이가 올라가게 된다.
     for (int i = 19; i >= 0; i--) {
          int diff = depth[y] - depth[x];
          if (diff >= (1 << i)) y = par[y][i];
     }
     
     // depth를 맞췄는데 같은 노드번호라면 리턴
     if (x == y) return x;
     
     // 부모가 같을테까지 같은 depth를 똑같이 올려준다
     for (int i = 19; i >= 0; i--) {
          if (par[x][i] != par[y][i]) {
               x = par[x][i];
               y = par[y][i];
          }
     }
     // 같아진 부모의 노드번호를 리턴해준다.
     return par[x][0];
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int N, M;
     cin >> N;
     for (int i = 0; i < N-1; i++) {
          int a, b;
          cin >> a >> b;
          vec[a].push_back(b);
          vec[b].push_back(a);
     }
     dfs(1, 0, 1);

     // 2 ^ 19 번째 부모는 입력조건보다 한참 크다, 딱 맞게(타이트하게) 부모의 깊이를 잡으려면
     // tree가 오른쪽으로만 늬여진 일렬로된 트리라고 가정한다면, (k = log(노드갯수)) 에서 par[노드수][k]로 설정하면 된다.
     for (int j = 1; j <= 19; j++) {
          for (int i = 1; i <= N; i++) {
               // i번 노드의 2^j번째 부모 = i의 2^(j-1)번쨰 부모의 2^(j-1) 부모
               // example )
               // par[1][1] = par[par[1][0]][0] : 1번 노드의 (2^1)번째 부모 = 1번노드의 (0)번째 부모의 (0)번째 부모( 즉 부모의 부모 )
               // par[1][2] = par[par[1][1]][1] : 1번 노드의 (2^2)번째 부모 = 1번노드의 (2^1)번쨰 부모의 (2^1)번째 부모 ( 즉, 증조부모의 증조부모 )
               // ...
               par[i][j] = par[ par[i][j - 1]][j - 1];
          }
     }
     cin >> M;
     for (int i = 0; i < M; i++) {
          int a, b; cin >> a >> b;
          cout << lca(a, b) << "\n";
     }
}

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 8012_한동이는 영업사원 (C++)  (0) 2019.10.02
[백준] 2269_수들의합 (C++)  (0) 2019.10.01
[백준] 2336_굉장한 학생 (C++)  (0) 2019.09.30

https://www.acmicpc.net/problem/5419

 

5419번: 북서풍

문제 강한 북서풍이 불고 있다. 이 뜻은 동쪽과 남쪽 사이의 모든 방향으로 항해할 수 있다는 뜻이다. 북쪽이나 서쪽으로 항해하는 것은 불가능하다. 작은 섬이 여러 개 있는 바다가 있다. 섬은 좌표 평면의 한 점으로 나타낼 수 있다. y 좌표가 증가하는 방향은 북쪽, x좌표가 증가하는 방향은 동쪽이다. 북서풍을 타고 항해할 수 있는 섬의 쌍의 수를 구하는 프로그램을 작성하시오. 입력 첫째 줄에 테스트 케이스의 개수가 주어진다. 각 테스트 케이스의 첫째 줄에

www.acmicpc.net


세그먼트 트리와 좌표압축을 통해서 해결 할 수 있다.

우선 pair< x, y > 형태로 입력을 모두 받은뒤에

x축정렬(같다면 y가 큰것부터)을 시행한다.

X축 정렬을 하고난뒤에는

k번째 좌표에서 k번째와 짝지어질 수 있는 좌표는 (0 ~ k-1) 좌표 중에 있을 것이다.

그 좌표들 중에서 k번째보다 y값이 큰것들이 해당 좌표에서 짝 지을 수 있는 좌표들이 된다.

x의 범위는 정렬을통해 인덱스로 체크 하면 되기때문에 상관 없지만 y의 범위가 

 (-10e9 ≤ xi, yi ≤ 10e9)

 이기 때문에 세그먼트 트리의 노드에 모든 y 값을 담을 수 없다.

그래서 좌표압축을 사용한다.

y만 담겨져 있는 vector를 입력때 하나 더만들어서 y축( 내림차순 ) 으로 입력받은뒤

upper_bound를 통해서 해당 y값이 몇번째로 큰 값인지 index를 확인해서

그 index가 결국 세그먼트의 target node index가 된다.

0~index까지 구간합을 결과값에 더해주면 된다.


#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define MAX_N 75001
int tree[MAX_N * 4];
vector<pair<int, int> > vec;
vector<int> y_vec;

void init_tree(int now, int start, int end) {
     if (start > end) return;
     if (start == end) {
          tree[now] = 0;
          return;
     }
     tree[now] = 0;
     int mid = (start + end) / 2;
     init_tree(now * 2, start, mid);
     init_tree(now * 2 + 1, mid + 1, end);
}

void update_tree(int now, int target, int start, int end) {
     if (target > end || target < start) return;
     if (start == end) {
          tree[now]++;
          return;
     }
     int mid = (start + end) / 2;
     update_tree(now * 2, target, start, mid);
     update_tree(now * 2 + 1, target, mid + 1, end);
     tree[now] = tree[now * 2] + tree[now * 2 + 1];
}

int query(int now, int left, int right, int start, int end) {
     if (left > end || right < start) return 0;
     if (left <= start && end <= right) {
          return tree[now];
     }
     int mid = (start + end) / 2;
     return (
          query(now * 2, left, right, start, mid) +
          query(now * 2 + 1, left, right, mid + 1, end)
          );
}

bool cmp_x_y(pair<int, int> a, pair<int, int> b) {
     if (a.first == b.first) return a.second > b.second;
     return a.first < b.first;
}

bool cmp_y(int a, int b) {
     return a > b;
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int T; cin >> T;
     while (T--) {
          int N; cin >> N;
          vec.clear();
          y_vec.clear();
          init_tree(1, 0, N - 1);
          for (int i = 0; i < N; i++) {
               int x, y; cin >> x >> y;
               vec.push_back({ x,y });
               y_vec.push_back(y);
          }
          long long  ret = 0;
          sort(vec.begin(), vec.end(),cmp_x_y);
          sort(y_vec.begin(), y_vec.end(), cmp_y);
          for (int i = 0; i < vec.size(); i++) {
               int x = vec[i].first, y = vec[i].second;
               int index = upper_bound(y_vec.begin(), y_vec.end(), y, cmp_y) - y_vec.begin() - 1;
               ret += query(1, 0, index, 0, N - 1);
               update_tree(1, index, 0, N - 1);
          }
          cout << ret << "\n";
     }
}

https://www.acmicpc.net/source/15427883

 

로그인

 

www.acmicpc.net


쿼리에서 b > c 인경우가 있다.


#include <iostream>
using namespace std;

#define swap(a,b) {int t = b; b = a; a = t;}
#define MAX_N 1000001
typedef long long ll;

ll tree[MAX_N * 4];

ll update_tree(int now, int target, int start, int end, int value) {
     if (target > end || target < start) return tree[now];
     if (start == end) {
          return tree[now] = value;
     }
     int mid = (start + end) / 2;
     return tree[now] = (
          update_tree(now * 2, target, start, mid, value) +
          update_tree(now * 2 + 1, target, mid + 1, end, value)
          );
}

ll query(int now, int left, int right, int start, int end) {
     if (left > end || right < start) return 0;
     if (left <= start && end <= right) {
          return tree[now];
     }
     int mid = (start + end) / 2;
     return (
          query(now * 2, left, right, start, mid) +
          query(now * 2 + 1, left, right, mid + 1, end)
          );
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int N, M;
     cin >> N >> M;
     for (int i = 0; i < M; i++) {
          int a, b, c;
          cin >> a >> b >> c;
          if (a) { // modify
               update_tree(1, b-1, 0, N - 1, c);
          }
          else { // sum
               if (c < b) swap(b, c);
               cout << query(1, b - 1, c - 1, 0, N - 1) << "\n";
          }
     }
}

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 11437_LCA (C++)  (0) 2019.10.02
[백준] 2336_굉장한 학생 (C++)  (0) 2019.09.30
[백준] 3392_화성지도 (C++)  (0) 2019.09.30

https://www.acmicpc.net/problem/2336

 

2336번: 굉장한 학생

문제 N명의 학생이 참여하여 세 번의 시험을 치렀다. N명의 학생들은 세 번의 시험에 모두 응시하였다. 조교는 각각의 시험에서 같은 등수의 학생이 한 명도 없도록 성적을 매겼다. A라는 학생이 B라는 학생보다 세 번의 시험에서 모두 성적이 좋다면, A가 B보다 '대단하다'고 한다. 또, C라는 학생보다 '대단한' 학생이 한 명도 없으면, C를 '굉장하다'고 한다. 세 번의 시험에서 각 학생의 성적이 주어졌을 때, '굉장한' 학생의 수를 구하는 프로그램을

www.acmicpc.net


정렬과 세그먼트트리를 이용하여 해결 할 수 있다.

 

입력 후, 첫번 째 등수에 대해서 x축 정렬한 결과.

1번 학생 : 0등 1등 7등

2번 학생 : 1등 4등 4등

3번 학생 : 2등 2등 0등

4번 학생 : 3등 7등 1등

5번 학생 : 4등 9등 3등

6번 학생 : 5등 6등 2등

7번 학생 : 6등 0등 6등

8번 학생 : 7등 5등 8등

9번 학생 : 8등 8등 9등

10번 학생 : 9등 3등 5등

 

 

로 정렬한뒤에 1번학생 ~ 10번 학생을 처리 하게 되는데,

1번학생은 반드시 굉장한 학생이 될 것이다.

2번학생은 오직 1번학생보다 하나만이라도 높다면 굉장한 학생이 될 것이다.

3번학생은 1,2번학생보다 등수가 모두 높다면 높다면 굉장한 학생이 될 것이다.

...

N번 학생은 N-1학생보다 등수가 다 높다면 굉장한 학생이 될 것이다.

< 1번 학생 >

1번째 리프노드 7로 update

< 2번 학생 >

0~3번 중 최솟값이 현재 7이므로 굉장한 학생이다

4번쨰 리프노드 4로 update

< 3번 학생 >

0~1번중 최솟값이 현재 7이므로 굉장한 학생이다

2번째 리프노드 0으로 update( 이학생 때문에 뒤의 학생들은 대부분 굉장한 학생이 아니게 된다 )

< 4번 학생 >

0~5번중 최솟값이 0이므로 이학생은 굉장한 학생이 아니다.

7번째 리프노드를 1로 update

.... 반복 ....


#include <iostream>
#include <algorithm>
using namespace std;

#define MAX_N 500000

class Info {
public :
     int x, y, z;
     Info() {}
     Info(int x, int y, int z) {
          this->x = x;
          this->y = y;
          this->z = z;
     }
     bool operator < (const Info & b) const {
          return this->x < b.x;
     }
};

Info arr[MAX_N];
int tree[MAX_N * 4];

void init_tree(int now, int start, int end) {
     if (start > end) return;
     if (start == end) {
          tree[now] = 1e9;
          return;
     }
     int mid = (start + end) / 2;
     tree[now] = 1e9;
     init_tree(now * 2, start, mid);
     init_tree(now * 2 + 1, mid + 1, end);
}

int update_tree(int now, int target, int start, int end, int value) {
     if (target > end || target < start) return tree[now];
     if (start == end) {
          return tree[now] = value;
     }
     int mid = (start + end) / 2;
     tree[now] = min(
          update_tree(now * 2, target, start, mid, value),
          update_tree(now * 2 + 1, target, mid + 1, end, value)
     );
     return tree[now];
}

int getMin(int now, int left, int right, int start, int end) {
     if (left > end || right < start) return 1e9;
     if (left <= start && end <= right) return tree[now];
     int mid = (start + end) / 2;
     return min(
          getMin(now * 2 , left, right, start, mid),
          getMin(now * 2 + 1, left, right, mid + 1, end)
     );
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int N; cin >> N;
     for (int i = 0; i < N; i++) {
          int t; cin >> t; t--; arr[t].x = i;
     }
     for (int i = 0; i < N; i++) {
          int t; cin >> t; t--; arr[t].y = i;
     }
     for (int i = 0; i < N; i++) {
          int t; cin >> t; t--; arr[t].z = i;
     }
     sort(arr, arr + N);
     init_tree(1,0,N-1);
     int ans = 0;
     for (int i = 0; i < N; i++) {
          int y = arr[i].y;
          int z = arr[i].z;
          if (getMin(1, 0, y, 0, N - 1) > z) ans++;
          update_tree(1, y, 0, N - 1, z);
     }
     cout << ans << "\n";

}

나한텐 너무 어려운 문제였지만, x,y,z를 묶어서 sort뒤의

출력물을 보고나서 쉽게 접근 할 수 있었다.

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준] 2269_수들의합 (C++)  (0) 2019.10.01
[백준] 3392_화성지도 (C++)  (0) 2019.09.30
[백준] 5676_음주 코딩 (C++)  (0) 2019.09.30

https://www.acmicpc.net/problem/3392

불러오는 중입니다...

처음 보는 세그먼트 트리의 유형에 굉장히 어려움을 겪었다.

밑의 글을 참고해서 풀어보았다.

모든 직사각형의 세로선분과 가로선분으로 생기는 부분을 나누어서 생각하는게 핵심이다.

 x축으로 정렬된 vector에는

4개의 선분이 들어가게된다

vector의 0번인덱스부터 2*n-1번 인덱스부터 선분하나하나씩을 처리해주면 된다.


#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define MAX_LEAF 30000

class Info {
public:
     int x, y1, y2;
     bool left;
     Info() {}
     Info(int x, int y1, int y2, bool left) {
          this->x = x;
          this->y1 = y1;
          this->y2 = y2;
          this->left = left;
     }
     bool operator < (const Info & b) const {
        return this->x < b.x;
     }
};

int tree[MAX_LEAF * 4];
int cnt[MAX_LEAF * 4];
void update_tree(int now, int left,int right, int start, int end, int value) {
     // 범위 체크
     if (left > end || right < start) return;

     // 해당 범위에 들어온다면 현재 cnt를 증가 또는 감소 시켜줌
     if (left <= start && end <= right) {
          cnt[now] += value;
     }

     // 범위에 들어올 지언정 " range_update " 를위해 하단 리프노드까지 내려가야 한다.
     else{
          int mid = (start + end) / 2;
          update_tree(now * 2, left, right, start, mid, value);
          update_tree(now * 2 + 1, left, right, mid+1, end, value);
     }

     // 해당 노드가 0 인데 리프노드라면 그냥 0으로 해주면되고
     //                   리프노드가 아니라면 ( 왼쪾자식 + 오른쪽자식 ) 이 자신의 cnt가 된다.
     if (!cnt[now]) {
          if (start != end)  tree[now] = tree[now * 2] + tree[now * 2 + 1];
          else  tree[now] = 0;
     }
     // 만약 자신이 해당범위에 들어와서 위에서 "cnt[now] += value" 로인해 1이상이 되었다면
     // 자기 하위의 모든 노드들은 범위에 포함되므로, 끝 - 시작 + 1 이 cnt가 된다.
     else  tree[now] = end - start + 1;
}


vector<Info> vec;
int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int N;
     cin >> N;
     for (int i = 0; i < N; i++) {
          int x1, y1, x2, y2;
          cin >> x1 >> y1 >> x2 >> y2;
          vec.push_back(Info(x1, y1, y2, true));
          vec.push_back(Info(x2, y1, y2, false));
     }
     // x축 정렬 ( for 라인스위핑 )
     sort(vec.begin(), vec.end());
     Info prev;

     int ans = 0;
     for (int i = 0; i < vec.size(); i++) {
          if (i) ans += (vec[i].x - vec[i - 1].x) * tree[1];
          // 왼쪽 선분인경우 : +1, 오른쪽선분인 경우 : -1
          int value = (vec[i].left == true) ? 1 : -1;
          update_tree(1, vec[i].y1, vec[i].y2 - 1, 0, MAX_LEAF - 1, value);
     }
     cout << ans << "\n";
}

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준] 2336_굉장한 학생 (C++)  (0) 2019.09.30
[백준] 5676_음주 코딩 (C++)  (0) 2019.09.30
[BOJ]7469_K번째 수(C++)  (0) 2019.09.30

https://www.acmicpc.net/problem/5676

 

5676번: 음주 코딩

문제 오늘은 ACM-ICPC 대회 전 날이다. 상근이는 긴장을 풀기 위해서 팀원들과 근처 술집으로 갔다. 상근이와 친구들은 다음 날 있을 대회를 연습하기 위해서 작은 게임을 하기로 했다. 먼저, 선영이는 상근이에게 총 N개의 정수로 이루어진 수열 X1, X2, ... XN을 적어준다. 게임은 총 K번 라운드로 이루어져있고, 매 라운드마다 선영이는 상근이에게 명령을 하나씩 내린다. 명령은 아래와 같이 두 가지가 있다. 변경: 이 명령은 친구가 수열의 한 값

www.acmicpc.net


세그먼트 트리에서 

음수의 갯수( 짝수 -> 양수, 홀수 -> 음수 )

0의 존재여부( 하나라도 존재한다면 0 )

의 파악이 핵심인 문제이다. "V =100" 인 숫자를 곱하다보면 금방 long long 범위에도 담기지 않기 때문이다.


#include <iostream>
#include <cstring>
using namespace std;

#define MAX_N 100001

// { 음수의갯수, 0의 존재여부 }
pair<int,bool> tree[MAX_N * 4];

void update_tree(int now, int target, int start, int end, int value) {
     if (target > end || target < start) return;
     if (start == end) {
          if (value == 0) 
              tree[now] = { 0,true };
          else if (value < 0) 
             tree[now] = { 1,false };
          else 
              tree[now] = { 0, false };
          return;
     }
     int mid = (start + end) / 2;
     update_tree(now * 2, target, start, mid, value);
     update_tree(now * 2 + 1, target, mid + 1, end, value);

     tree[now].first = tree[now * 2].first + tree[now * 2 + 1].first;
     (tree[now * 2].second || tree[now * 2 + 1].second)? tree[now].second = true : tree[now].second = false;
}

pair<int, bool> query(int now, int left, int right, int start, int end) {
     if (left > end || right < start) return {0,false};
     if (left <= start && end <= right) {
          return tree[now];
     }
     int mid = (start + end) / 2;
     pair<int, bool> ret = { 0,false };
     pair<int, bool> left_ret = query(now * 2, left,right, start, mid);
     pair<int, bool> right_ret = query(now * 2 + 1, left, right, mid + 1, end);
     ret.first = left_ret.first + right_ret.first;
     (left_ret.second == true || right_ret.second == true)?ret.second = true: ret.second = false;
     return ret;
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int N, K;
     while (cin >> N >> K) {
          for (int i = 0; i <= 4 * N; i++) {
               tree[i].first = 0;
               tree[i].second = false;
          }
          for (int i = 0; i < N; i++) {
               int data; cin >> data;
               update_tree(1, i, 0, N - 1, data);
          }
          for (int i = 0; i < K; i++) {
               char tag; cin >> tag;
               if (tag == 'C') {
                    int a, b; cin >> a >> b;
                    update_tree(1, a - 1, 0, N - 1, b);
               }
               else {
                    int a, b; cin >> a >> b;
                    pair<int, bool> ret = query(1, a - 1, b - 1, 0, N - 1);
                    if (ret.second) cout << "0";
                    else {
                         if (ret.first % 2 == 1) cout << "-";
                         else cout << "+";
                    }
               }
          }
          cout << "\n";
     }
}

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준] 3392_화성지도 (C++)  (0) 2019.09.30
[BOJ]7469_K번째 수(C++)  (0) 2019.09.30
[백준]1275_커피숍2(C++)  (0) 2019.09.29

https://www.acmicpc.net/problem/7469

 

7469번: K번째 수

문제 현정이는 자료 구조 프로젝트를 하고 있다. 다른 학생들은 프로젝트 주제로 스택, 큐와 같은 기본 자료 구조를 구현하는 주제를 선택했다. 하지만, 현정이는 새로운 자료 구조를 만들었다. 현정이가 만든 자료구조는 배열을 응용하는 것이다. 배열 a[1...n]에는 서로 다른 수가 n개 저장되어 있다. 현정이는 여기에 Q(i,j,k)라는 함수를 구현해 모두를 놀라게 할 것이다. Q(i,j,k): 배열 a[i...j]를 정렬했을 때, k번째 수를 리턴하는 함

www.acmicpc.net


트리의 노드는 하위 노드들의 값의 정렬된 배열형태로 저장되어야 하고.

파라매트릭 서치를 이용해서 값을 찾아내야 시간초과에 걸리지 않는다.

파라매트릭 서치의 아이디어는 생각해내지 못해서 구글링을통해 겨우 알았다..ㅠ


#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define MAX_N 100001
vector<int> tree[MAX_N*4]; // 트리는 vector 형태로 정렬되서 자기 하위 노드들의 값이 저장되어 있음.

void update_tree(int now, int target, int start, int end, int value) {
     if (target > end || target < start) return;
     if (start == end) {
          tree[now].push_back(value);
          return;
     }
     // merge sort 를 사용한 init 을 사용하면 더 좋겠지만, 큰차이 없으므로 그냥 main 함수에서 for문으로 sort해주었음.
     tree[now].push_back(value);
     int mid = (start + end) / 2;
     update_tree(now * 2, target, start, mid, value);
     update_tree(now * 2 + 1, target, mid + 1, end, value);
}

// index를 리턴해준다
int query(int now, int left, int right, int start, int end,int k) {
     if (left > end || right < start)  {
          return 0;
     }
     if (left <= start && end <= right) {
          return upper_bound(tree[now].begin(), tree[now].end(), k) - tree[now].begin();
     }
     int mid = (start + end) / 2;
     return query(now * 2, left, right, start, mid, k) + query(now * 2 + 1, left, right, mid + 1, end, k);
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int n, Q;
     cin >> n >> Q;
     for (int i = 0; i < n; i++) {
          int data; cin >> data;
          update_tree(1, i, 0, n - 1, data);
     }

     for (int i = 0; i <= 4 * n; i++) {
          sort(tree[i].begin(), tree[i].end());
     }

     for (int i = 0; i < Q; i++) {
          int s, e, x;
          cin >> s >> e >> x;
          // 문제에서 나타나있는 최대범위를 이용해서 파라매트릭 서치의 최소와 최댓값을 정해준다
          int MIN = -1e9, MAX = 1e9;
          while (MIN <= MAX) {
               int MID = (MIN + MAX) / 2;
               // s ~ e 값사이의 MID 값이 k번쨰 숫자일 경우
               // k < x : mid값을 높여 줘야 k가 x이상이 나올 수 있다.
               // k > x : mid값을 낮춰 줘야 k가 x이하가 나올 수 있다.
               int k = query(1, s-1, e-1, 0, n-1, MID);
               if (k < x) MIN = MID + 1;
               else MAX = MID - 1;
          }
          // else 구문에서 " k == x " 인경우에 계속 숫자를 낮춰 줬기 때문에, MIN 값이 곧 답이 된다.
          cout << MIN << "\n";
     }
}

겨우겨우 풀어냈고, 다른 분들의 풀이를 보고 다른 방법도 익혀봐야 겠다.

 

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준] 5676_음주 코딩 (C++)  (0) 2019.09.30
[백준]1275_커피숍2(C++)  (0) 2019.09.29
[백준]3653_영화 수집(C++)  (0) 2019.09.29

https://www.acmicpc.net/problem/11505

 

11505번: 구간 곱 구하기

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 회수이고, K는 구간의 곱을 구하는 회수이다. 그리고 둘째 줄부터 N+1번째 줄까지 N개의 수가 주어진다. 그리고 N+2번째 줄부터 N+M+K+2번째 줄까지 세 개의 정수 a,b,c가 주어지는데, a가 1인 경우 b번째 수를 c (0 ≤ c ≤ 1,000,000)로 바꾸고 a가 2인 경우에

www.acmicpc.net


ll query(int now, int left, int right, int start, int end) {
     if (left > end || right < start) return 1;
     if (left <= start && end <= right) return tree[now];
     int mid = (start + end) / 2;
     return (query(now * 2, left, right, start, mid) * query(now * 2 + 1, left, right, mid + 1, end)) % DIV;
}

범위에서 벗어낫을때 return 1 을 해주는것이 이문제의 핵심이고, 알면 바로 풀 수 있다.

xor, + , *, -, & 연산 등등 문제에서 요구하는 연산이 무엇인지 잘 파악하고 함수를 짜야한다.

#include <iostream>
using namespace std;

#define MAX_N 1000001
#define DIV 1000000007
typedef long long ll;

ll tree[MAX_N * 4];

ll update_tree(int now, int target, int start, int end, int value) {
     if (target > end || target < start) return tree[now];
     if (start == end) return tree[now] = value;
     int mid = (start + end) / 2;
     return tree[now] = (update_tree(now * 2, target, start, mid, value) * update_tree(now * 2 + 1, target, mid + 1, end, value)) % DIV;
}

ll query(int now, int left, int right, int start, int end) {
     if (left > end || right < start) return 1;
     if (left <= start && end <= right) return tree[now];
     int mid = (start + end) / 2;
     return (query(now * 2, left, right, start, mid) * query(now * 2 + 1, left, right, mid + 1, end)) % DIV;
}


int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int N, M, K;
     cin >> N >> M >> K;
     for (int i = 0; i < N; i++) {
          int data; cin >> data;
          update_tree(1, i, 0, N - 1, data);
     }
     for (int i = 0; i < M + K; i++) {
          int a, b, c;
          cin >> a >> b >> c;
          if (a == 1) {
               update_tree(1, b - 1, 0, N - 1, c);
          }
          else {
               cout << query(1, b - 1, c - 1, 0, N - 1) << "\n";
          }
     }
}

+ Recent posts