#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<long long> tree;
vector<long long> a;
int N, Q;
// 세그먼트 트리 구축
void build() {
for (int i = 0; i < N; ++i) {
tree[N + i] = a[i];
}
for (int i = N - 1; i > 0; --i) {
tree[i] = tree[i << 1] + tree[i << 1 | 1];
}
}
// 세그먼트 트리의 특정 위치 값 업데이트
void updateTree(int where, long long value) {
where += N;
tree[where] = value;
while (where > 1) {
where >>= 1;
tree[where] = tree[where << 1] + tree[where << 1 | 1];
}
}
// 세그먼트 트리 구간 합 쿼리
long long query(int left, int right) {
long long sum = 0;
left += N;
right += N;
while (left <= right) {
if (left & 1) sum += tree[left++];
if (!(right & 1)) sum += tree[right--];
left >>= 1;
right >>= 1;
}
return sum;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> N >> Q;
a.resize(N);
tree.resize(2 * N);
for (int i = 0; i < N; ++i) {
cin >> a[i];
}
build();
for (int i = 0; i < Q; ++i) {
int x, y, a_idx;
long long b;
cin >> x >> y >> a_idx >> b;
if (x > y) swap(x, y);
cout << query(x - 1, y - 1) << '\n';
updateTree(a_idx - 1, b);
}
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
const int MAX = 100001;
int parent[MAX];
int Find(int a) {
if (parent[a] == a) return a;
return parent[a] = Find(parent[a]); // 경로 압축 기법
}
void Union(int a, int b) {
a = Find(a);
b = Find(b);
if (a != b) parent[b] = a; // 부모를 갱신하여 연결
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int N, M;
cin >> N >> M;
vector<pair<int, pair<int, int>>> edges; // {weight, {u, v}}
// 부모 초기화
for (int i = 1; i <= N; i++) {
parent[i] = i;
}
for (int i = 0; i < M; i++) {
int u, v, weight, d;
cin >> u >> v >> weight >> d;
if (d == 1) {
Union(u, v);
}
else {
edges.push_back({ weight, {u, v} });
}
}
int total_weight = 0;
// 간선을 가중치 기준으로 내림차순 정렬
sort(edges.begin(), edges.end(), greater<pair<int, pair<int, int>>>());
for (const auto& edge : edges) {
int weight = edge.first;
int u = edge.second.first;
int v = edge.second.second;
if (Find(u) != Find(v)) {
Union(u, v);
}
else {
total_weight += weight;
}
}
cout << total_weight << "\n";
return 0;
}
특히 3차원 벡터를 사용하기때문에 실수하기 쉽기 때문에 체감난이도가 조금 더 높게 느껴 질 수 있다.
#전체 코드
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#define MAX 52
using namespace std;
struct fireBall {
int y;
int x;
int mass;
int speed;
int dir;
};
int N, M, K;
vector<fireBall> mapVec[MAX][MAX];
int dx[8] = { 0,1,1,1,0,-1,-1,-1 };
int dy[8] = { -1,-1,0,1,1,1,0,-1 };
// 경계 조정 함수
int isBoundary(int x) {
if (x > N) {
x = x % N;
}
if (x < 1) {
x = N + (x % N);
}
return x;
}
void moveFireBall() {
vector<fireBall> temp[MAX][MAX];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (mapVec[i][j].size() == 0) {
continue;
}
else {
for (int k = 0; k < mapVec[i][j].size(); k++) {
int mass = mapVec[i][j][k].mass;
int speed = mapVec[i][j][k].speed;
int dir = mapVec[i][j][k].dir;
int ny = isBoundary(dy[dir] * speed + mapVec[i][j][k].y);
int nx = isBoundary(dx[dir] * speed + mapVec[i][j][k].x);
temp[ny][nx].push_back({ ny,nx,mass,speed,dir });
}
mapVec[i][j].clear();
}
}
}
// 격자 상태 업데이트
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
mapVec[i][j] = temp[i][j];
}
}
}
void sumFireBall() {
vector<fireBall> temp[MAX][MAX];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (mapVec[i][j].size() == 0) {
continue;
}
else if (mapVec[i][j].size() == 1) {
temp[i][j] = mapVec[i][j];
}
else {
int sumMass = 0;
int sumSpeed = 0;
bool sameDir = true;
for (int t = 0; t < mapVec[i][j].size(); t++) {
sumMass += mapVec[i][j][t].mass;
sumSpeed += mapVec[i][j][t].speed;
if (t > 0 && mapVec[i][j][t].dir % 2 != mapVec[i][j][t - 1].dir % 2) {
sameDir = false;
}
}
sumMass /= 5;
sumSpeed /= mapVec[i][j].size();
if (sumMass == 0) {
continue;
}
for (int d = 0; d < 4; d++) {
if (sameDir) {
temp[i][j].push_back({ i, j, sumMass, sumSpeed, d * 2 });
}
else {
temp[i][j].push_back({ i, j, sumMass, sumSpeed, d * 2 + 1 });
}
}
}
}
}
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
mapVec[i][j] = temp[i][j];
}
}
}
int main() {
cin >> N >> M >> K;
for (int i = 0; i < M; i++) {
int y, x, m, s, d;
cin >> y >> x >> m >> s >> d;
mapVec[x][y].push_back({ y,x,m,s,d });
}
for (int i = 0; i < K; i++) {
moveFireBall();
sumFireBall();
}
int result = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
for (int k = 0; k < mapVec[i][j].size(); k++) {
result += mapVec[i][j][k].mass;
}
}
}
cout << result << '\n';
return 0;
}
#설계
int main() {
cin >> N >> M >> K;
for (int i = 0; i < M; i++) {
int y, x, m, s, d;
cin >> y >> x >> m >> s >> d;
mapVec[x][y].push_back({ y,x,m,s,d });
}
for (int i = 0; i < K; i++) {
moveFireBall();
sumFireBall();
}
int result = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
for (int k = 0; k < mapVec[i][j].size(); k++) {
result += mapVec[i][j][k].mass;
}
}
}
cout << result << '\n';
return 0;
}
우선 시작할때 생각 할 수 있는것은
1. 파이어볼을 이동시키기
2. 파이어볼을 나누기
두가지 큰 로직으로 나눌 수 있기 때문에
k번 반복하는
moveFireBall();
sumFireBall();
두개를 구현한다고 생각한다.
#define MAX 52
using namespace std;
struct fireBall {
int y;
int x;
int mass;
int speed;
int dir;
};
int N, M, K;
vector<fireBall> mapVec[MAX][MAX];
int dx[8] = { 0,1,1,1,0,-1,-1,-1 };
int dy[8] = { -1,-1,0,1,1,1,0,-1 };
// 경계 조정 함수
int isBoundary(int x) {
if (x > N) {
x = x % N;
}
if (x < 1) {
x = N + (x % N);
}
return x;
}
이후에 파이어볼의 정보를 담을 파이어볼의 구조체와
각 맵의 N을 넘어가거나 0이하가 되었을때 양 옆을 이어주기 위한
isBoundary() 함수를 작성한다.
isBoundary 함수는 단순히 N넘어갔을때 더하기 연산을 구현해도 되지만
모듈러 연산으로 구현했을 때 좀 더 깔끔 한 것 같다.
#이동 함수 구현
void moveFireBall() {
vector<fireBall> temp[MAX][MAX];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (mapVec[i][j].size() == 0) {
continue;
}
else {
for (int k = 0; k < mapVec[i][j].size(); k++) {
int mass = mapVec[i][j][k].mass;
int speed = mapVec[i][j][k].speed;
int dir = mapVec[i][j][k].dir;
int ny = isBoundary(dy[dir] * speed + mapVec[i][j][k].y);
int nx = isBoundary(dx[dir] * speed + mapVec[i][j][k].x);
temp[ny][nx].push_back({ ny,nx,mass,speed,dir });
}
}
}
}
// 격자 상태 업데이트
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
mapVec[i][j] = temp[i][j];
}
}
}
우선 이동한 파이어볼들의 정보를 담을
vector<fireBall> temp[MAX][MAX];
임시 벡터를 선언해주고
for (int k = 0; k < mapVec[i][j].size(); k++) {
int mass = mapVec[i][j][k].mass;
int speed = mapVec[i][j][k].speed;
int dir = mapVec[i][j][k].dir;
int ny = isBoundary(dy[dir] * speed + mapVec[i][j][k].y);
int nx = isBoundary(dx[dir] * speed + mapVec[i][j][k].x);
temp[ny][nx].push_back({ ny,nx,mass,speed,dir });
}
만약 파이어볼이 들어있다면
int ny = isBoundary(dy[dir] * speed + mapVec[i][j][k].y);
int nx = isBoundary(dx[dir] * speed + mapVec[i][j][k].x);
경계를 조사하며 파이어볼의 위치와 속도에 따라 갱신한 새 파이어볼을 입력해준다.
// 격자 상태 업데이트
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
mapVec[i][j] = temp[i][j];
}
}
그리고 이동한 파이어볼의 정보가 들어있는 temp 벡터는 mapVec에 복사해주도록 한다.
#나누기 함수 구현
void sumFireBall() {
vector<fireBall> temp[MAX][MAX];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
if (mapVec[i][j].size() == 0) {
continue;
}
else if (mapVec[i][j].size() == 1) {
temp[i][j] = mapVec[i][j];
}
else {
int sumMass = 0;
int sumSpeed = 0;
bool sameDir = true;
for (int t = 0; t < mapVec[i][j].size(); t++) {
sumMass += mapVec[i][j][t].mass;
sumSpeed += mapVec[i][j][t].speed;
if (t > 0 && mapVec[i][j][t].dir % 2 != mapVec[i][j][t - 1].dir % 2) {
sameDir = false;
}
}
sumMass /= 5;
sumSpeed /= mapVec[i][j].size();
if (sumMass == 0) {
continue;
}
for (int d = 0; d < 4; d++) {
if (sameDir) {
temp[i][j].push_back({ i, j, sumMass, sumSpeed, d * 2 });
}
else {
temp[i][j].push_back({ i, j, sumMass, sumSpeed, d * 2 + 1 });
}
}
}
}
}
for (int d = 0; d < 4; d++) {
if (sameDir) {
temp[i][j].push_back({ i, j, sumMass, sumSpeed, d * 2 });
}
else {
temp[i][j].push_back({ i, j, sumMass, sumSpeed, d * 2 + 1 });
}
}
넣어줄 방향에 따라서 적절히 나눈 파이어볼을 넣는다.
이후에 벡터 복사도 똑같이 해준 후에
main 함수로 넘어가서 질량의 총합을 구해주면 된다.
int result = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
for (int k = 0; k < mapVec[i][j].size(); k++) {
result += mapVec[i][j][k].mass;
}
}
}
#결론
어려워하지 말고 천천히 구현해보도록 하자.
파이어볼의 정보 기록을 위해서 3차원 벡터를 사용하는 연습을 하기 좋은 문제라고 생각한다.
스택 사용: 스택을 사용하여 지금까지 만난 블록의 높이 중 높은 블록들의 인덱스를 저장합니다.
높이 비교: 새로운 블록을 만날 때마다 스택의 top에 있는 블록의 높이와 비교합니다.
새로운 블록의 높이가 스택의 top에 있는 블록보다 낮다면, 새로운 블록의 인덱스를 스택에 push합니다.
새로운 블록의 높이가 스택의 top에 있는 블록의 높이보다 크거나 같다면, 스택에서 pop을 수행하고 그 사이에 빗물이 고일 수 있는지 검사합니다.
빗물 계산: 스택에서 두 블록을 pop하여 그 사이에 물이 고일 수 있는지 확인합니다. 물이 고일 수 있는 조건은 다음과 같습니다:
현재 블록 (오른쪽)과 스택에서 pop한 블록 (왼쪽) 사이에 적어도 하나의 블록이 있어야 하며 (즉, 공간이 있어야 하며)
물이 고일 높이는 두 블록 높이의 최소값에서 그 사이 블록의 최대 높이를 빼준 값입니다.
*GPT 씨의 친절한 설명
#전체 코드
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int H, W;
cin >> H >> W;
vector<int> blocks(W);
for (int i = 0; i < W; i++) {
cin >> blocks[i];
}
vector<int> left_max(W), right_max(W);
left_max[0] = blocks[0];
for (int i = 1; i < W; i++) {
left_max[i] = max(left_max[i - 1], blocks[i]);
}
right_max[W - 1] = blocks[W - 1];
for (int i = W - 2; i >= 0; i--) {
right_max[i] = max(right_max[i + 1], blocks[i]);
}
int waterResult = 0;
for (int i = 0; i < W; i++) {
waterResult += min(left_max[i], right_max[i]) - blocks[i];
}
cout << waterResult << "\n";
return 0;
}
스택 사용 코드
#include <iostream>
#include <stack>
#include <vector>
int main() {
int H, W;
std::cin >> H >> W;
std::vector<int> heights(W);
for (int i = 0; i < W; ++i) {
std::cin >> heights[i];
}
std::stack<int> st;
int totalWater = 0;
// 블록들을 순회하면서 물이 고일 수 있는지 확인
for (int i = 0; i < W; ++i) {
// 현재의 높이가 스택에 있는 높이보다 크거나 같을 때까지 검사
while (!st.empty() && heights[st.top()] < heights[i]) {
int top = st.top();
st.pop();
// 스택이 비었다면 왼쪽 벽이 없음
if (st.empty())
break;
int distance = i - st.top() - 1;
int bounded_height = std::min(heights[i], heights[st.top()]) - heights[top];
totalWater += distance * bounded_height;
}
// 현재 인덱스를 스택에 추가
st.push(i);
}
std::cout << totalWater << std::endl;
return 0;
}