※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 13519번 문제인 트리와 쿼리 10이다.
문제는 아래 링크를 확인하자.

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

 

13519번: 트리와 쿼리 10

N개의 정점으로 이루어진 트리(무방향 사이클이 없는 연결 그래프)가 있다. 정점은 1번부터 N번까지 번호가 매겨져 있고, 간선은 1번부터 N-1번까지 번호가 매겨져 있다. 정점은 가중치를 가지고

www.acmicpc.net

트리 위에서의 경로 내 최대 연속합 쿼리와 경로 업데이트 쿼리를 빠르게 처리하는 문제이다.

 

배열 위에서의 범위 내 최대 연속합을 구하는 방법은 잘 알려져있다는 점을 상기하면, 해당 문제를 배열 위에서 해결하는 세그먼트트리와 HLD(heavy-light decomposition)를 이용해 문제를 해결할 수 있다는 것을 알아낼 수 있다.

 

구현량이 매우 많으므로 실수 없이 잘 구현하자.

 

아래는 제출한 소스코드이다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;

int N, Q;

struct node {
	ll val, lval, rval, tval;
	node() {};
	node(ll val, ll lval, ll rval, ll tval) {
		this->val = val, this->lval = lval, this->rval = rval, this->tval = tval;
	}
};

node nodemerge(node& nodeL, node& nodeR) {
	ll retV = max(nodeL.rval + nodeR.lval, max(nodeL.val, nodeR.val));
	ll retL = max(nodeL.lval, nodeL.tval + nodeR.lval);
	ll retR = max(nodeR.rval, nodeR.tval + nodeL.rval);
	ll retT = nodeL.tval + nodeR.tval;
	return node(retV, retL, retR, retT);
}

ll prearr[100001];
ll arr[100001];
node seg[262145];
ll lazy[262145];
node init(int L, int R, int sI) {
	if (L == R) {
		ll& tmp = arr[L];
		if (tmp < 0) return seg[sI] = node(0, 0, 0, tmp);
		else return seg[sI] = node(tmp, tmp, tmp, tmp);
	}

	node nodeL = init(L, (L + R) / 2, sI * 2), nodeR = init((L + R) / 2 + 1, R, sI * 2 + 1);
	return seg[sI] = nodemerge(nodeL, nodeR);
}
void propagate(int L, int R, int sI) {
	ll& tmp = lazy[sI]; tmp -= 65536;
	if (tmp < 0) seg[sI] = node(0, 0, 0, tmp * (R - L + 1));
	else seg[sI] = node(tmp * (R - L + 1), tmp * (R - L + 1), tmp * (R - L + 1), tmp * (R - L + 1));
	if (L < R) lazy[sI * 2] = lazy[sI * 2 + 1] = tmp + 65536;
	lazy[sI] = 0;
}
node upd(int L, int R, int qL, int qR, ll qVal, int sI) {
	if (lazy[sI]) propagate(L, R, sI);
	if (R < qL || qR < L) return seg[sI];
	if (qL <= L && R <= qR) {
		lazy[sI] = qVal + 65536;
		propagate(L, R, sI);
		return seg[sI];
	}
	node nodeL = upd(L, (L + R) / 2, qL, qR, qVal, sI * 2), nodeR = upd((L + R) / 2 + 1, R, qL, qR, qVal, sI * 2 + 1);
	return seg[sI] = nodemerge(nodeL, nodeR);
}
node qry(int L, int R, int qL, int qR, int sI) {
	if (lazy[sI]) propagate(L, R, sI);
	if (R < qL || qR < L) return node(0, 0, 0, 0);
	if (qL <= L && R <= qR) return seg[sI];

	node nodeL = qry(L, (L + R) / 2, qL, qR, sI * 2), nodeR = qry((L + R) / 2 + 1, R, qL, qR, sI * 2 + 1);
	return nodemerge(nodeL, nodeR);
}

vector<int> G[100001];
int childcnt[100001];
int heavychild[100001];

int dfs1(int cur, int par) {
	int ret = 0;
	int heavycnt = 0;
	for (auto node : G[cur]) {
		if (node == par) continue;
		int tmp = dfs1(node, cur);
		if (tmp > heavycnt) heavychild[cur] = node, heavycnt = tmp;
		ret += tmp;
	}
	childcnt[cur] = ret;
	return ret + 1;
}

int nodeidx = 1, chainidx = 1;
int dfi[100001];
//아래 배열들은 dfi기준
int chainnum[100001]; // 현재 노드는 몇번 체인에 속해있는가
int chainnth[100001]; // 현재 노드는 속해있는 체인의 몇번째(0-based) 원소인가
int chainlevel[100001]; // 현재 노드가 포함된 체인의 깊이는 몇인가
int chainparent[100001]; // 현재 노드가 포함된 체인은 어디(dfi)에 붙어있는가

void dfs2(int cur, int par, int cpar, int nth, int lv) {
	int idx = dfi[cur] = nodeidx++;
	arr[idx] = prearr[cur];
	chainnum[idx] = chainidx;
	chainnth[idx] = nth;
	chainlevel[idx] = lv;
	chainparent[idx] = cpar;

	int& hchild = heavychild[cur];
	if (hchild) dfs2(hchild, cur, cpar, nth + 1, lv);

	for (auto node : G[cur]) {
		if (node == par || node == hchild) continue;
		chainidx++;
		dfs2(node, cur, idx, 0, lv + 1);
	}
}

void solve() {
	int q; cin >> q;
	if (q == 1) {
		int u, v; cin >> u >> v;
		u = dfi[u], v = dfi[v];
		if (chainlevel[u] > chainlevel[v]) swap(u, v);
		node nodeU = node(0, 0, 0, 0), nodeV = node(0, 0, 0, 0);
		while (chainlevel[u] < chainlevel[v]) {
			node nodeQ = qry(1, N, v - chainnth[v], v, 1);
			nodeV = nodemerge(nodeQ, nodeV);
			v = chainparent[v];
		}
		while (chainnum[u] != chainnum[v]) {
			node nodeQ = qry(1, N, u - chainnth[u], u, 1);
			nodeU = nodemerge(nodeQ, nodeU);
			nodeQ = qry(1, N, v - chainnth[v], v, 1);
			nodeV = nodemerge(nodeQ, nodeV);
			u = chainparent[u];
			v = chainparent[v];
		}
		if (u > v) swap(u, v), swap(nodeU, nodeV);
		node nodeQ = qry(1, N, u, v, 1);
		swap(nodeU.lval, nodeU.rval);
		nodeU = nodemerge(nodeU, nodeQ);
		nodeU = nodemerge(nodeU, nodeV);
		cout << nodeU.val << '\n';
	}
	else {
		int u, v; ll qVal; cin >> u >> v >> qVal;
		u = dfi[u], v = dfi[v];
		if (chainlevel[u] > chainlevel[v]) swap(u, v);
		while (chainlevel[u] < chainlevel[v]) {
			upd(1, N, v - chainnth[v], v, qVal, 1);
			v = chainparent[v];
		}
		while (chainnum[u] != chainnum[v]) {
			upd(1, N, u - chainnth[u], u, qVal, 1);
			upd(1, N, v - chainnth[v], v, qVal, 1);
			u = chainparent[u];
			v = chainparent[v];
		}
		if (u > v) swap(u, v);
		upd(1, N, u, v, qVal, 1);
	}
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	cin >> N;
	for (int i = 1; i <= N; i++) cin >> prearr[i];
	for (int i = 1; i < N; i++) {
		int x, y;  cin >> x >> y;
		G[x].emplace_back(y);
		G[y].emplace_back(x);
	}
	dfs1(1, 0);
	dfs2(1, 0, 0, 0, 1);
	init(1, N, 1);

	cin >> Q;
	while (Q--) solve();
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 1658 // C++] 돼지 잡기  (0) 2022.07.30
[BOJ 11495 // C++] 격자 0 만들기  (0) 2022.07.29
[BOJ 13557 // C++] 수열과 쿼리 10  (0) 2022.07.27
[BOJ 15957 // C++] 음악 추천  (0) 2022.07.26
[BOJ 8217 // C++] 유성  (0) 2022.07.25

+ Recent posts