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

 

10000번: 원 영역

문제 x축 위에 원이 N개 있다. 원은 서로 교차하지 않는다. 하지만, 접할 수는 있다. 원으로 만들어지는 영역이 몇 개인지 구하는 프로그램을 작성하시오. 영역은 점의 집합으로 모든 두 점은 원을 교차하지 않는 연속되는 곡선으로 연결될 수 있어야 한다. 입력 첫째 줄에 원의 개수 N(1 ≤ N ≤ 300,000)이 주어진다. 다음 N개 줄에는 각 원의 정보 xi와 ri가 주어진다. xi는 원의 중심 좌표이며, ri는 반지름이다. (-109 ≤ xi ≤ 1

www.acmicpc.net


문제해설

"라인 스위핑"을 염두에둔 상태에서 문제를 잘 읽으면

조건에 의해 풀 수있는 방법이 떠오르는 문제이다( 저는 상당히 오래걸렸습니다 ㅠ )

 

위의 빨간박스가 이문제의 핵심이며 라인스위핑을 통해서 답을 구해낼 수 있는 이유라고 할 수 있습니다.

쉽게 생각해서

원의 왼쪽 : 스택의 왼쪽괄호

원의 오른쪽 : 스택의 오른쪽괄호

라고 생각하면 됩니다.

왼쪽괄호가 열렸다는 것은 언젠가 자신보다 오른쪽 좌표에서 오른쪽 괄호가 나타난다는 것을 의미합니다.

스택의 열고 닫기를 하는 와중에 닫는 동작을 할 때에, 중간에 구멍없이 계속 접해왔는지 확인화면 되는 문제입니다.

 

< 스택 >

class stackItem {
public:
	ll pos; int state;
	stackItem(ll pos, int state) {
		this->pos = pos;
		this->state = state;
	}
}; stack<stackItem> st;

스택에 pos와 state 라는 변수를 두었습니다.

pos : 좌표값을 의미합니다.

state 

초기 괄호를 열였을 경우 : 0

열고나서 끊김없이 계속 접해온경우 : 1

열고나서 끊긴 적이 있는경우 : -1

세가지의 경우를 나누었습니다.

 

여러 케이스를 통해 예시를 들어 보겠습니다

 

< 예시 1 >

위 처럼 원이 하나만 존재하는 경우는 괄호로 표현시 () 가 됩니다.

 

 

위와 같이 stack에 state 0 이 들어가게되고

) 에 의해 pop이 되는경우 ans++ 를 해주게 됩니다.


< 예시 2 >

위와같이 큰원 안에 작은원이 두개가 완전히 접한다고 가정해봅시다

괄호로 표현하면 (()()) 가 될 것입니다. 위 그림이 어떻게 답이 5가 되는지 확인 해봅시다.

 

위의 왼쪽 괄호 (( 두개를 처리한뒤의 스택의 모습입니다. 물론 pos 정보도 포함하고 있겠죠.

top 에있는 item은 방금 스택에 들어간 item이고

그밑의 item은 push되는 item과 좌표값이 겹치기 때문에 state가 1로 변경된 item입니다.

 

그다음 ) 가 들어갔을 경우는 ( ) 가 짝이 지어짐으로 pop이 되고, state가 0 이였으므로 ans++를 해주게 됩니다.

 

 

똑같이 그다음 ) 가 들어갔을 경우는 ( ) 가 짝이 지어짐으로 pop이 되고, state가 0 이였으므로 ans++를 해주게 됩니다.

 

마지막의 경우는 state가 1인 상태임으로 답이 2가 나오게 됩니다.

총합 답이 5가 나오게 됩니다.


 

< 예시 3 >

위의 그림의 경우는 스택의 모양으로는 ( ()() ) 가 될 것입니다. 예시 2과 스택의 구조는 같지만

item의 state를 변경해줌으로써 다른 답이 나오게 됩니다.

 

아까와 다르게 밑의 item의 state가 -1이 된 모습을 확인할 수 있습니다. 

처리하는 과정은 ( 괄호를 추가하였을때 top 에있는 pos와 현재 pos와 다르다면 

top에있는 item의 state를 -1로 바꾸어 주었습니다.

그후 현재 정보를 push 해주면 됩니다.

이후 부터는 예시2와 같이 처리해주면 답이 4가 나오게 됩니다.


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

bool cmp(pll a, pll b) {
	if (a.first == b.first) return a.second > b.second;
	else return a.first < b.first;
}
class stackItem {
public:
	ll pos; int state;
	stackItem(ll pos, int state) {
		this->pos = pos;
		this->state = state;
	}
}; stack<stackItem> st;

vector<pair<ll, int>> 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++) {
		ll x, r; cin >> x >> r;
		vec.push_back({ x - r,-1 });
		vec.push_back({ x + r, 1 });
	}
	sort(vec.begin(), vec.end(), cmp);

	ll ans = 0; ll last = 0;
	for (int i = 0; i < vec.size(); i++) {
		if (st.empty()) {
			st.push(stackItem(vec[i].first, 0));
			last = vec[i].first;
		}
		else if (vec[i].second == -1) {
			if (vec[i].first == last) {
				vector<stackItem> vec_tmp;
				stackItem tmp = st.top(); st.pop();
				if (tmp.state != -1) tmp.state = 1;
				st.push(tmp);
				st.push(stackItem(vec[i].first, 0));
			}
			else {
				stackItem tmp = st.top(); st.pop();
				tmp.state = -1;
				st.push(tmp);
				st.push(stackItem(vec[i].first, 0));
				last = vec[i].first;
			}
		}
		else if (vec[i].second == 1) {
			stackItem tmp = st.top(); st.pop();
			if (tmp.state == 1 && last == vec[i].first)
				ans += 2;
			else
				ans++;

			last = vec[i].first;
		}
	}
	cout << ans + 1 << "\n";
}

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

[백준BOJ] 2188 축사 배정  (0) 2019.10.13
[백준BOJ] 3078 좋은 친구  (2) 2019.10.10
[BOJ백준] 2170 선 긋기  (0) 2019.10.10

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

 

3078번: 좋은 친구

문제 상근이는 환갑을 바라보던 나이에 수능 시험을 다시보고 교대에 입학했고, 초등학교 선생님으로 취직했다. 상근: 요즘 애들은 친구를 사귀지 않나봐. 내가 앞에서 보고 있으면, 친구가 있는 학생이 별로 없는 것 같아. ??: 오빠! 오빠는 말콤의 친구와 성적이라는 책 안 읽어 봤어? 이 책에는 성적과 친구가 무슨 관계가 있는지 나와. 요즘 애들은 친구를 사귀기 전에 먼저 그 친구의 반 등수를 살펴봐. 말콤은 이 연구를 하기 위해서 6년동안 초등학교에서 선

www.acmicpc.net


문제해설

입력의 갯수가 30만이 이기 때문에, 전수조사를 할경우 O(N^2)롤 TLE에 걸리게 됩니다.

O(NlgN) 정도의 알고리즘이 필요해 보입니다.

 

<입력처리>

	// 이름의 길이에 등수를 push_back
	for (int i = 0; i < N; i++) {
		string input; cin >> input;
		vec[input.size()].push_back(i + 1);
	}

입력이 문자열로 들어오지만, 저희가 필요한 정보는 문자열의 길이와 등수뿐 입니다.

 

 

<정렬>

이문제의 핵심이라고 할 수 있는 부분입니다.

이름 길이가 같은 친구끼리 묶인 vector에서 정렬을 통해서

이후에, 선형 조사를 하게 되던, lower_bound, upper_bound를 하게되던 정렬을 해야만 

큰 입력에 대해서 시간안에 답을 구할 수 있습니다.

	// i 길이의 이름을 가진 친구들을 등수별로 sort
	for (int i = 0; i < 21; i++) {
		sort(vec[i].begin(), vec[i].end());
	}

<upper_bound>

i 길이의 이름을 가진 친구중에서 j번째 등수기준으로

j + K 번째 등수까지 몇명이 있는지 알기위해서

이분탐색을통해서 알 수 있습니다.

	for (int i = 0; i < 21; i++) {
		// upper_bound를 편의를 위한 push_back
		vec[i].push_back(1e9);
		for (int j = 0; j < vec[i].size()-1; j++) {
			// 자신의 등수보다 K 보다 큰 마지막 친구가 iter로 반환 됩니다.
			auto iter = upper_bound(vec[i].begin(), vec[i].end(), vec[i][j] + K); iter--;
			// 시작 주소를 빼줌으로써 index값을 얻을 수 있고
			auto index = iter - vec[i].begin();
			// 마지막 친구의 index - 현재 index를 해주면 친구의 명수가 나오게 됩니다.
			ans += index - j;
		}
	}

전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;

vector<int> vec[21];

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N, K; cin >> N >> K;

	// 이름의 길이에 등수를 push_back
	for (int i = 0; i < N; i++) {
		string input; cin >> input;
		vec[input.size()].push_back(i + 1);
	}

	// i 길이의 이름을 가진 친구들을 등수별로 sort
	for (int i = 0; i < 21; i++) {
		sort(vec[i].begin(), vec[i].end());
	}

	long long ans = 0;
	for (int i = 0; i < 21; i++) {
		// upper_bound를 편의를 위한 push_back
		vec[i].push_back(1e9);
		for (int j = 0; j < vec[i].size()-1; j++) {
			// 자신의 등수보다 K 보다 큰 마지막 친구가 iter로 반환 됩니다.
			auto iter = upper_bound(vec[i].begin(), vec[i].end(), vec[i][j] + K); iter--;
			// 시작 주소를 빼줌으로써 index값을 얻을 수 있고
			auto index = iter - vec[i].begin();
			// 마지막 친구의 index - 현재 index를 해주면 친구의 명수가 나오게 됩니다.
			ans += index - j;
		}
	}
	cout << ans << "\n";
}

 

 

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

[BOJ백준] 10000 원 영역  (0) 2019.10.13
[BOJ백준] 2170 선 긋기  (0) 2019.10.10
[백준BOJ] 13306 트리  (0) 2019.10.09

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

 

2170번: 선 긋기

첫째 줄에 선을 그은 횟수 N(1≤N≤1,000,000)이 주어진다. 다음 N개의 줄에는 선을 그을 때 선택한 두 점이 주어진다. 선택한 지점은 -1,000,000,000 이상 1,000,000,000 이하의 정수이다.

www.acmicpc.net


문제해설

이 문제의 분류는 "라인스위핑" 입니다.

라인스위핑 알고리즘을 사용하여 중구난방으로 입력된 좌표들을 

어떤 기준에 의해 정렬한뒤에

O(N)으로 처음부터 끝까지 쓰~윽 처리해주시면 됩니다.

기준을 잡는 것에 있어서 여러가지 방법이 있을 수 있습니다.

저는 x축 을 기준으로 정렬한뒤에 현재까지 선을 그은 마지막 위치를 저장하여 처리 했습니다. 

 


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;

typedef pair<int, int> pii;
vector<pii> 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 a, b; cin >> a >> b;
		if (b < a) {
			int t = b;
			b = a;
			a = t;
		}
		vec.push_back({ a,b });
	}
	int e = -(1e9+1);
	int ans = 0;
	sort(vec.begin(), vec.end());
	for (int i = 0; i < N; i++) {
		if (e == -(1e9 + 1)) {
			e = vec[i].second;
			ans += e - vec[i].first;
		}

		if (vec[i].first < e) {
			int diff = vec[i].second - e;
			ans += (diff > 0) ? diff : 0;
			e = max(e,vec[i].second);
		}
		else if (vec[i].first >= e) {
			ans += vec[i].second - vec[i].first;
			e = vec[i].second;
		}
	}
	cout << ans << "\n";
}

 

 

 

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

[백준BOJ] 3078 좋은 친구  (2) 2019.10.10
[백준BOJ] 13306 트리  (0) 2019.10.09
[BOJ백준] 5574 산책  (0) 2019.10.09

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

 

13306번: 트리

표준 입력으로 다음 정보가 주어진다. 첫 번째 줄에는 트리의 정점의 개수와 질의의 개수를 나타내는 두 정수 N과 Q (1 ≤ N, Q ≤ 200,000)가 주어진다. 다음 N-1개의 줄의 i번째 줄에는 정점 i+1의 부모 정점을 나타내는 정수 a가 주어진다 (1 ≤ a ≤ N). 다음 (N-1)+Q개의 줄 중에서 N-1개는 (1)의 형태로, Q개는 (2)의 형태로 주어진다. (1) 두 정수 x와 b가 주어진다(x = 0, 2 ≤ b ≤ N). 이것은 b의

www.acmicpc.net


문제해설

이 문제의 아이디어는 설렁설렁 문제를 읽어서는 떠오르기 쉽지 않은 문제입니다.

이 문제는 disjoint-set 및 union-find 알고리즘을 사용한다는 것 까지는 쉽게 알 수 있습니다.

그러나 union-find 의연산횟수가 적은 이유는

int find(int x) {
	if (par[x] == x) {
		return x;
	}
	return par[x] = find(par[x]);
}

위 처럼, find를 할때에 union의 root를 부모로 설정해주기 때문입니다.

 

그러나,

이문제에서는 자신의 바로 윗부모와의 엣지를 삭제하는 쿼리가 들어옵니다.

위의 코드가 전혀 쓸모없는 코드가 되어버리고

저코드를 활용한다하더라도

int find(int x) {
	if (par[x] == x) {
		return x;
	}
	return find(par[x]);
}

위 처럼, 딱봐도 TL이 걸릴 것 같은 코드정도로 밖에 쓰지 못할 것입니다.

 

union-find의 특성을 그대로 활용하기 위해서

쿼리를 뒤에서부터 처리해주는 방법을 선택할 수 있습니다.

제일 뒤에서부터 시작하기 때문에, 아무런 연결이 없는상태로 시작해서

삭제되는 쿼리를 만나면 union을 시켜주는 형태로 문제를 풀어 나가면 됩니다.


전체코드

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

class Input {
public:
	int tag, x, y, par;
	Input() {};
	Input(int tag, int x, int y) {
		this->tag = tag;
		this->x = x;
		this->y = y;
	}
	Input(int tag, int x) {
		this->tag = tag;
		this->x = x;
	}
};

int par[200001];
int origin[200001];
vector<string> ans;
vector<Input> input;

int find(int x) {
	if (par[x] == x) {
		return x;
	}
	return par[x] = find(par[x]);
}

void make_set(int x, int p) {
	x = find(x);
	p = find(p);
	par[x] = p;
	//cout << x << "의부모 : " << par[x] << endl;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int N, Q;
	cin >> N >> Q;
	par[1] = 1;
	for (int i = 2; i <= N; i++) {
		cin >> origin[i];
		par[i] = i;
	}


	for (int i = 0; i < N + Q - 1; i++) {
		int tag;  cin >> tag;
		if (tag) {
			int x, y; cin >> x >> y;
			input.push_back(Input(1, x, y));
		}
		else {
			int x; cin >> x;
			input.push_back(Input(0, x));
		}
	}

	vector <string> ans;
	for (int i = N + Q - 2; i >= 0; i--) {
		if (input[i].tag) {
			int x = input[i].x;
			int y = input[i].y;
			if (find(x) == find(y)) ans.push_back("YES");
			else ans.push_back("NO");
		}
		else {
			int x = input[i].x;
			make_set(x, origin[x]);
		}
	}

	for (int i = Q - 1; i >= 0; i--) {
		cout << ans[i] << "\n";
	}
}

 

 

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

[BOJ백준] 2170 선 긋기  (0) 2019.10.10
[BOJ백준] 5574 산책  (0) 2019.10.09
[백준BOJ] 10774 저지  (0) 2019.10.07

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

 

5573번: 산책

문제 상근이는 건강을 위해 산책을 하려고 한다. 상근이가 사는 마을은 아래 그림과 같이 가로 방향 도로가 (H+1)개, 세로 방향 도로가 (W+1)개가 바둑판 모양으로 배치되어 있다. 상근이네 집은 가장 왼쪽 위 교차로에 있으며, 이곳에서 산책을 시작한다. (a,b)는 위쪽에서 a번째, 왼쪽에서 b번째에 있는 교차로이다. 예를 들어, 상근이네 집은 교차로 (1,1)에 있다. 상근이는 산책 경로가 매일 달라야 질리지 않고 산책을 할 수 있다고 생각한다. 따

www.acmicpc.net


문제해설

H와 W의 입력자체가 1000까지 들어오지만, N이 10^7까지 들어오기 때문에,

일반적인 시뮬레이션으로는 해결 할 수없어 보입니다.

총 N번 산책을 했을 때, 어떤 교차점에대해서 몇번 지나게 되는가를 미리 계산하면 됩니다.

(1,1) 정점은 N번 산책을 할 경우 N번 지나게 될 것입니다.

(1,2)는 대략적으로 N/2

(2,1)도 대략적으로 N/2

...

이런식으로 dp 테이블을 생성해 나아가면 됩니다.

 

< 예시 >

W = 1, H = 1 인 케이스에서 N 이 주어졌다고 생각해봅시다.

N번 산책동안 1,1를 N번 지나게됨으로

N번 산책일때 오른쪽으로 가는가, 왼쪽으로 가는가만 계산하면 될텐데

첫 입력에서 (오른쪽)이 주어졌다면

짝수번 방문할 경우는 (2,1)

홀수번 방문할 경우는(1,2)

에 도착하게됩니다.

 

W = 2, H = 2인 케이스에서 N이 주어졌다고 생각해봅시다.

N이만약 짝수라면

dp[1][1] = N번

dp[2][1] = dp[1][1]/2

dp[1][2] = dp[1][1]/2

dp[2][2] = dp[1][2]/2 + dp[2][1]/2

가 될 것입니다. 

 

위의 같은케이스에서 N이홀 수라면 

처음 입력에 따라서 오른쪽이나 아래쪽에 1이 더큰 숫자가 들어가게 될 것입니다.


 DP 테이블을 완성한뒤에

mod2 연산을 하게 되었을때 dp 테이블에 남은 숫자가 곧

방향을 알려줄 것이고

DFS를 통해 결과를 구 할 수 있습니다.


전체코드

#include <iostream>
using namespace std;

int arr[1001][1001];
int dp[1001][1001];
int R, C, N;
pair<int, int> ans;

void DFS(int r, int c) {
	if (r >= R || c >= C) {
		ans = { r,c };
		return;
	}
	if (arr[r][c] == 1) {
		if (dp[r][c] == 1) 
			DFS(r, c + 1);
		else 
			DFS(r + 1, c);
	}
	else {
		if (dp[r][c] == 1)
			DFS(r+1, c);
		else 
			DFS(r, c+1);
	}
}


int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> R >> C >> N;
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			cin >> arr[i][j];
		}
	}

	dp[0][0] = N;
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			if (arr[i][j] == 1) {
				dp[i+1][j] += dp[i][j] / 2;
				dp[i][j+1] += (dp[i][j] % 2 == 0) ? dp[i][j] / 2 : dp[i][j] / 2 + 1;
			}
			else {
				dp[i+1][j] += (dp[i][j] % 2 == 0) ? dp[i][j] / 2 : dp[i][j] / 2 + 1;
				dp[i][j + 1] += dp[i][j] / 2;
			}
		}
	}

	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			dp[i][j] %= 2;
		}
	}
	DFS(0, 0);
	cout << ++ans.first  << " " << ++ans.second << "\n";
}

 

 

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

[백준BOJ] 13306 트리  (0) 2019.10.09
[백준BOJ] 10774 저지  (0) 2019.10.07
[백준BOJ] 5012 불만 정렬  (0) 2019.10.07

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

 

10774번: 저지

문제 학교 대표팀은 1부터 번호가 매겨진 저지를 학생 선수들에게 배분하고자 한다. 저지의 사이즈는 S, M, L 중 하나이다 (물론 S=small, M=medium, L=Large다). 각각의 선수들은 구체적인 저지의 번호와 선호하는 사이즈를 요구했다. 선수들은 만약 자신이 원했던 번호가 아니거나, 선호하는 사이즈보다 작은 사이즈의 옷을 받으면 불만이 생길 것이다. 그들을 만족시키기 위해서는, 요구하는 번호가 맞고 사이즈는 같거나 그 이상이어야 한다. 두

www.acmicpc.net


문제해설

이 문제의 분류가 Disjoint-set 인데 , 진짜 일차원적인 Disjoint-set 인문제같습니다.

그냥 if로 경우 잘나누면 되는 문제입니다.


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;

#define MAX_J 1000000
char arr[MAX_J + 1];

int main() {
     ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
     int J; cin >> J;
     int A; cin >> A;
     for (int i = 1; i <= J; i++) {
          cin >> arr[i];
     }
     int ans = 0;
     for (int i = 0; i < A; i++) {
          char tag; int num;
          cin >> tag >> num;
          if (arr[num] == 'S') {
               if (tag == 'S') {
                    arr[num] = 'X';
                    ans++;
               }
          }
          else if (arr[num] == 'M') {
               if ( tag == 'M' || tag == 'S') {
                    arr[num] = 'X';
                    ans++;
               }
          }
          else if (arr[num] == 'L') {
               if (tag == 'L' || tag == 'M' || tag == 'S') {
                    arr[num] = 'X';
                    ans++;
               }
          }
     }
     cout << ans << "\n";
}

 

 

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

[BOJ백준] 5574 산책  (0) 2019.10.09
[백준BOJ] 5012 불만 정렬  (0) 2019.10.07
[백준BOJ] 3006 터보소트  (0) 2019.10.06

 

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

 

5012번: 불만 정렬

문제 정렬 알고리즘의 상한(upper bound)은 n2이다. 이 사실은 쉽게 증명할 수 있다. 올바른 순서가 아닌 임의의 두 원소(ai > aj, i < j)를 선택하고, 그 위치를 서로 바꿔준다. 이렇게 올바른 순서가 아닌 것을 도치(inversion)라고 하며, 도치의 개수는 최대 n(n-1)/2개이다.  현주는 사회에 대한 불만이 많은 아이이다. 그는 항상 정렬을 할 때, 두 원소를 선택하는 것에도 큰 불만을 가지고 있다. 현주는 ai > aj >

www.acmicpc.net


문제해설

"펜윅 트리"를 사용하여 풀었습니다.

이 문제는 밑의 문제와 굉장히 유사한 문제라고 할 수 있습니다.

2019/10/06 - [알고리즘/BOJ] - [백준BOJ] 3006 터보소트

 

[백준BOJ] 3006 터보소트

https://www.acmicpc.net/problem/3006 3006번: 터보소트 문제 명우가 소트 알고리즘을 하나 발명했다. 이 알고리즘의 이름은 터보소트이다. 터보소트는 1부터 N까지 총 N개의 수가 섞여있을 때만 사용할 수 있으..

polohee81.tistory.com

i < j < k 이면서 Ai > Aj > Ak

인 경우의 수를 찾기 위해서는

j를 기준으로 왼쪽, 오른쪽을 탐색하면 됩니다.

 

< 변수 >

#define MAX_N 100000
int tree_left[MAX_N + 1];
int tree_right[MAX_N + 1];
vector<int> l, r;

 

case1 : 자신 기준 왼쪽에 있으면서 큰 값의 수

case2 : 자신 기준 오른쪽에 있으면서 작은 값의 수

< case1 >

int left =  query(tree_left, MAX_N) - query(tree_left, data_left);

위의 코드처럼 구간합으로 자신보다 왼쪽에 있으면서 큰 값들의 갯수를 구할 수 있습니다.

 

< case2 >

int right =  query(tree_right,  data_right-1);

위의 코드처럼 구간합으로 자신보다 오른쪽에 있으면서 작은 값들의 갯수를 구할 수 있습니다. 


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
vector<int> vec;
#define MAX_N 100000
int tree_left[MAX_N + 1];
int tree_right[MAX_N + 1];
vector<int> l, r;

void update(int tree[],int target, int value) {
     while (target <= MAX_N) {
          tree[target] += value;
          target += (target & -target);
     }
}

int query(int tree[],int target) {
     int ret = 0;
     while (target >= 1) {
          ret += tree[target];
          target -= (target &-target);
     }
     return ret;
}

int main() {
     ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
     int N;
     cin >> N;
     for (int i = 1; i <= N; i++) {
          int data; cin >> data;
          vec.push_back(data);
     }

     long long ans = 0;
     for (int i = 0, j = vec.size()-1; i < vec.size(); i++, j--) {
          int data_left = vec[i]; 
          int data_right = vec[j];

          int right =  query(tree_right,  data_right-1);
          int left =  query(tree_left, MAX_N) - query(tree_left, data_left);
          l.push_back(left); r.push_back(right);

          update(tree_right, data_right, 1);
          update(tree_left, data_left, 1);

     }
     for (int i = 0, j = r.size()-1; i < l.size(); i++,j--) {
          ans += ((long long)l[i] * r[j]);
     }
     cout << ans << "\n";
}

 

 

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

[백준BOJ] 10774 저지  (0) 2019.10.07
[백준BOJ] 3006 터보소트  (0) 2019.10.06
[백준BOJ] 1280 나무 심기  (0) 2019.10.06

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

 

3006번: 터보소트

문제 명우가 소트 알고리즘을 하나 발명했다. 이 알고리즘의 이름은 터보소트이다.  터보소트는 1부터 N까지 총 N개의 수가 섞여있을 때만 사용할 수 있으며, 다음과 같이 N단계로 이루어져 있다. 첫 번째 단계에서 숫자 1의 위치를 찾는다. 그 다음 바로 앞의 숫자와 위치를 바꾸어가면서, 1이 제일 앞에 오게 바꾼다. 두 번째 단계에서는 숫자 N의 위치를 찾는다. 그 다음 바로 뒤의 숫자와 위치를 바꾸어가면서, N이 제일 마지막에 오게 바꾼다. 세 번째 단

www.acmicpc.net


문제해설

정답률이 무려 70% 라서 여러풀이가 가능한 것 같습니다. 저는 "펜윅 트리"를 사용한 풀이를 해설 하도록 하겠습니다.

     for (int i = 1; i <= N; i++) {
          int data; cin >> data;
          update(i, 1);
          pos[data] = i;
     }

update는 위와같이 단지 자리를 1로 update 해주었고,

해당 data가 어느위치에 삽입되었는지 pos 배열에 저장 해주었습니다.

     for (int i = 1, j = N; i <= j; i++,j--) {
          int minPos = pos[i];
          int maxPos = pos[j];
          cout << query(minPos-1) << "\n";
          update(minPos, -1);
          if (i != j) {
               cout << query(N) - query(maxPos) << "\n";
               update(maxPos, -1);
          }
     }

제일작은값(1...)

제일 큰값(N...)

처리를 위해서 i++, j--를 이용함과 동시에 같다면 하나의 인덱스만 처리하고 반복문을 종료 하였습니다.

위의 코드는 해당 숫자보다 앞에 있는 숫자의 갯수를 출력하고

해당 숫자는 자기 자리를 찾아가게 되었으니 -1로 update처리 해주었습니다.


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;

#define MAX_N 100000
int tree[MAX_N + 1];
int pos[MAX_N + 1];

void update(int target, int value) {
     while (target <= MAX_N) {
          tree[target] += value;
          target += (target & -target);
     }
}

int query(int target) {
     int ret = 0;
     while (target >= 1) {
          ret += tree[target];
          target -= (target &-target);
     }
     return ret;
}

int main() {
     ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
     int N; cin >> N;

     for (int i = 1; i <= N; i++) {
          int data; cin >> data;
          update(i, 1);
          pos[data] = i;
     }

     for (int i = 1, j = N; i <= j; i++,j--) {
          int minPos = pos[i];
          int maxPos = pos[j];
          cout << query(minPos-1) << "\n";
          update(minPos, -1);
          if (i != j) {
               cout << query(N) - query(maxPos) << "\n";
               update(maxPos, -1);
          }
     }
}

 

 

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

[백준BOJ] 5012 불만 정렬  (0) 2019.10.07
[백준BOJ] 1280 나무 심기  (0) 2019.10.06
[백준BOJ] 11658 구간 합 구하기3  (0) 2019.10.06

+ Recent posts