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/1275

 

1275번: 커피숍2

첫째 줄에 수의 개수 N과 턴의 개수 Q가 주어진다.(1 ≤ N, Q ≤ 100,000) 둘째 줄에는 처음 배열에 들어가 있는 정수 N개가 주어진다. 세 번째 줄에서 Q+2번째 줄까지는 x y a b의 형식으로 x~y까지의 합을 구하여라, a번째 수를 b로 바꾸어라 라는 뜻의 데이터가 주어진다. 입력되는 모든 수는 32비트 부호있는 정수이다.

www.acmicpc.net

문제 풀이만 읽으면 그냥 세그먼트트리 구성하고

(쿼리, update)함수를 쓸줄아냐 모르냐 정도를 묻는 문제인데

정답률이 많이 낮았는데 역시 그럴만한 이유가 있었다.

 

x > y 인경우는 함수에 들어가자마 리턴이 되기 때문에 함수가 돌지 못한다.

#include <iostream>
using namespace std;
#define SWAP(a,b) {int t; t=a; a=b; b=t;}
#define MAX_N 100001

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, 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 < Q; i++) {
          int x, y, a, b;
          cin >> x >> y >> a >> b;
          if (y < x) SWAP(x, y);
          cout << query(1, x - 1, y - 1, 0, N - 1) << "\n";
          update_tree(1, a - 1, 0, N - 1, b);
     }
}

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

[BOJ]7469_K번째 수(C++)  (0) 2019.09.30
[백준]3653_영화 수집(C++)  (0) 2019.09.29
[백준]7578_공장(C++)  (0) 2019.09.29

세그먼트 트리를 사용해서 해결 했습니다.

트리의 리프노드의 갯수를 n + m 개로 셋팅한뒤에

현재 k번째 리프노드가 제일위의 책의 위치라면

다음 위로올라가는 책은 k + 1 번째의 리프노드에 업데이트 해주었습니다.

최대 tree의 배열크기를 (10만 + 10만) * 4 + 1 로 해주어야 20만개의 리프노드를 처리가능합니다.


tree의 0번째 노드 : 제일밑에있는 책의 인덱스

tree의 n-1번째 노드 : 제일 위에있는 책의 인덱스

query로 자신보다 오른쪽에 있는 값을 리턴하고

자기 자신이 있던 노드를 0으로 업데이트해준뒤

유효한 리프노드 + 1 번째( 올라가게되는 위치 ) 를 1로 업데이트 해줍니다.

 

#include <iostream>
using namespace std;

#define MAX_N 100001
#define MAX_M 100001
int tree[MAX_N * 8]; 
int ans[MAX_M], arr[MAX_N];

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

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);
}

int main() {
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int T; cin >> T;
     while (T--) {
          int n, m;
          cin >> n >> m;
          int size = n + m - 1;
          for (int i = 0; i < n+m; i++) {
               if (i < n)
                    update_tree(1, i, 0, size, 1);
               else
                    update_tree(1, i, 0, size, 0);
               
          }
          for (int i = n; i >= 1; i--) {
               arr[i] = (n - i);
          }
       
          for (int i = 0; i < m; i++) {
               int data; cin >> data;
               int target = arr[data];
               ans[i] = query(1, target+1, size, 0, size);
               update_tree(1, target, 0, size, 0);
               update_tree(1, n, 0, size, 1);
               arr[data] = ++n;
          }
          for (int i = 0; i < m; i++) {
               cout << ans[i] << " ";
          }
          cout << "\n";
     }
}

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

[BOJ]7469_K번째 수(C++)  (0) 2019.09.30
[백준]1275_커피숍2(C++)  (0) 2019.09.29
[백준]7578_공장(C++)  (0) 2019.09.29

세그먼트 트리를 사용해서 풀었습니다.

세그먼트 분류 에서 단지 암기한 코드를 쓰기 바빴는데

이문제가 어떻게 세그먼트 트리 알고리즘이 적용될지 고민을 한참 했습니다 ㅜㅜ.

리프노드의 K번째를 지정해서 update 할 수 있다는 세그먼트 트리의 특징을 사용했습니다 !

주의점으로 답이 int 범위에 담기지 않습니다.

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

#include <iostream>
using namespace std;

#define MAX_N 500000
int map[1000001];
int tree[MAX_N * 4];

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

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);
}

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 data; cin >> data;
          map[data] = i;
     }
     long long ans = 0;
     for (int i = 0; i < N; ++i) {
          int data; cin >> data;
          int target = map[data];
          ans += query(1, target, N - 1, 0, N - 1);
          update_tree(1, target, 0, N - 1);
     }
     cout << ans << "\n";
}

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

[BOJ]7469_K번째 수(C++)  (0) 2019.09.30
[백준]1275_커피숍2(C++)  (0) 2019.09.29
[백준]3653_영화 수집(C++)  (0) 2019.09.29

+ Recent posts