Algorithm
Problem Name: Data Structures -
In this HackerRank in Data Structures -
Our lazy white falcon finally decided to learn heavy-light decomposition. Her teacher gave an assignment for her to practice this new technique. Please help her by solving this problem.
You are given a tree with N nodes and each node's value is initially 0. The problem asks you to operate the following two types of queries:
- "1 u x" assign x to the value of the node u.
- "2 u v" print the maximum value of the nodes on the unique path between u and v.
Input Format
First line consists of two integers seperated by a space: N and Q.
Following N - 1 lines consisting of two integers denotes the undirectional edges of the tree.
Following Q lines consist of the queries you are asked to operate.
Constraints
1 <= N,Q,x <= 50000
It is guaranteed that input denotes a connected tree with N nodes. Nodes are enumerated with 0-based indexing.
Output Format
For each second type of query print single integer in a single line, denoting the asked maximum value.
Sample Input
3 3
0 1
1 2
1 0 1
1 1 2
2 0 2
Sample Output
2
Explanation
After the first two updates value of the 0th node is 1 and 1st node is 2. That is why maxiumum value on the path between 0 and 2 is max(1,2) = 2.
Code Examples
#1 Code Example with C Programming
Code -
C Programming
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <inttypes.h>
typedef struct link {
struct node1 *with;
struct link *next;
} link;
typedef struct node1 {
int id;
int mark;
int num_connections;
link *connections;
} node1;
typedef struct segment {
struct segment *parent, *left, *right;
int height; //sideways and grows towards the root
int low_depth, high_depth; //low is numerically *greater*, low==high when leaf
int max_val;
} segment;
typedef struct node {
struct node *parent;
struct node *chain_head;
int sub_size; //includes self
int num_children;
struct node **children;
segment seg; //contains val and depth
} node;
node **out_storage = NULL;
node ***child_storage = NULL;
node **mapping = NULL;
node * directify(node1 *at, node *out_parent, int depth) {
//assert(!at->mark);
at->mark = 1;
node *out = *out_storage;
(*out_storage)++;
out->seg.max_val = 0;
out->seg.low_depth = depth;
out->seg.high_depth = depth;
out->seg.height = 0;
out->seg.left = out->seg.right = out->seg.parent = NULL;
out->parent = out_parent;
out->num_children = at->num_connections - 1;
out->children = (out->num_children == 0) ? NULL : *child_storage;
out->sub_size = 1;
(*child_storage) += out->num_children;
mapping[at->id] = out;
int ci = 0;
for (link *l = at->connections; l; l = l->next) {
node1 *to = l->with;
if (!to->mark) {
node *child = directify(to, out, depth + 1);
out->children[ci++] = child;
out->sub_size += child->sub_size;
}
}
assert(ci == out->num_children);
return out;
}
typedef struct segment_block {
struct segment_block *prev;
int use;
segment segs[1024];
} segment_block;
int max(int a, int b) {
return (a > b) ? a : b;
}
segment_block *front_block = NULL;
segment * new_segment(segment *left_child, segment *right_child) {
if (!front_block || front_block->use == 1024) {
segment_block *last = front_block;
front_block = (segment_block*)calloc(1, sizeof(segment_block));
front_block->prev = last;
front_block->use = 0;
}
segment *seg = &front_block->segs[front_block->use++];
//assert(left_child && right_child);
//assert(left_child->low_depth >= left_child->high_depth);
//assert(right_child->low_depth >= right_child->high_depth);
//assert(left_child->high_depth > right_child->low_depth);
seg->low_depth = left_child->low_depth;
seg->high_depth = right_child->high_depth;
seg->height = max(left_child->height, right_child->height) + 1;
seg->left = left_child;
seg->right = right_child;
seg->max_val = max(left_child->max_val, right_child->max_val);
seg->parent = NULL;
left_child->parent = seg;
right_child->parent = seg;
return seg;
}
void build_chain_tree(node *final, node *chain_head, int chain_length) {
static segment **stack = NULL;
static int stack_length = 0;
if (stack_length < chain_length) {
stack_length = chain_length;
stack = (segment**)realloc(stack, stack_length * sizeof(segment*));
}
node *at = final;
int stack_size = 0;
while (at && at->chain_head == chain_head) {
stack[stack_size] = &at->seg;
stack_size++;
at = at->parent;
while (stack_size > 1) {
segment *r = stack[stack_size - 1];
segment *l = stack[stack_size - 2];
if (r->height == l->height) {
segment *p = new_segment(l, r);
stack[stack_size - 2] = p;
stack_size--;
}
else {
break;
}
}
}
//assert(stack_size >= 1);
while (stack_size > 1) {
segment *r = stack[stack_size - 1];
segment *l = stack[stack_size - 2];
//assert(r->height < = l->height);
segment *p = new_segment(l, r);
stack[stack_size - 2] = p;
stack_size--;
}
}
void hld(node *at, node *chain_head, int chain_length, int *num_chains) {
if (!chain_head) {
chain_head = at;
(*num_chains)++;
}
at->chain_head = chain_head;
node *most = NULL;
for (int ci = 0; ci < at->num_children; ci++) {
if (!most || most->sub_size < at->children[ci]->sub_size) {
most = at->children[ci];
}
}
for (int ci = 0; ci < at->num_children; ci++) {
node *ch = at->children[ci];
if (ch == most) {
//continue chain
hld(ch, chain_head, chain_length + 1, num_chains);
}
else {
//start new chain
hld(ch, NULL, 1, num_chains);
}
}
if (!most) {
//end of chain, build tree
build_chain_tree(at, chain_head, chain_length);
}
}
node * lca(node *u, node *v) {
while (u->chain_head != v->chain_head) {
if (u->chain_head->seg.low_depth < v->chain_head->seg.low_depth)
v = v->chain_head->parent;
else
u = u->chain_head->parent;
}
return (u->seg.low_depth < v->seg.low_depth) ? u : v;
}
int segment_range_query(segment *at, int low_depth, int high_depth) {
//assert(low_depth >= high_depth);
//assert(at->low_depth >= low_depth);
//assert(at->high_depth < = high_depth);
if (at->low_depth == low_depth && at->high_depth == high_depth) {
return at->max_val;
}
else {
if (high_depth >= at->left->high_depth) {
return segment_range_query(at->left, low_depth, high_depth);
}
else if (low_depth < = at->right->low_depth) {
return segment_range_query(at->right, low_depth, high_depth);
}
else {
return max(segment_range_query(at->left, low_depth, at->left->high_depth),
segment_range_query(at->right, at->right->low_depth, high_depth));
}
}
}
int path_max_inner(node *higher, node* lower) {
int64_t max_val = 0;
//assert(lower && higher);
while (lower->chain_head != higher->chain_head) {
segment *seg = &lower->seg;
while (seg->parent) seg = seg->parent;
max_val = max(max_val, segment_range_query(seg, lower->seg.low_depth, seg->high_depth));
lower = lower->chain_head->parent;
}
//assert(lower && higher);
segment *seg = &lower->seg;
while (seg->parent) seg = seg->parent;
max_val = max(max_val, segment_range_query(seg, lower->seg.low_depth, higher->seg.high_depth));
return max_val;
}
int path_max(node *u, node *v) {
if (u == v) {
return u->seg.max_val;
}
else {
node *pivot = lca(u, v);
//assert(pivot);
//assert(pivot->seg.low_depth < = u->seg.low_depth);
//assert(pivot->seg.low_depth <= v->seg.low_depth);
int ml = path_max_inner(pivot, u);
int mr = path_max_inner(pivot, v);
return max(ml, mr);
}
}
void segment_update(segment *at) {
while (at) {
at->max_val = max(at->left->max_val, at->right->max_val);
at = at->parent;
}
}
int main() {
int n, q;
scanf("%d %d", &n, &q);
node1 *initial = (node1*)calloc(n, sizeof(node1));
link *links = (link*)malloc(2 * (n - 1) * sizeof(link));
for (int i = 0; i < n; i++) {
initial[i].id = i;
}
//store links
for (int i = 0; i < n - 1; i++) {
int ai, bi;
scanf("%d %d", &ai, &bi);
node1* a = &initial[ai];
node1* b = &initial[bi];
link* la = &links[2 * i + 0];
link* lb = &links[2 * i + 1];
la->next = a->connections;
lb->next = b->connections;
la->with = b;
lb->with = a;
a->num_connections++;
b->num_connections++;
a->connections = la;
b->connections = lb;
}
node **child_links = (node**)malloc((n - 1) * sizeof(node*));
node *processed = (node*)calloc(n, sizeof(node));
mapping = (node**)calloc(n, sizeof(node*));
//convert to tree, node 0 as root
node *procp = processed; out_storage = &procp;
node **clp = child_links; child_storage = &clp;
initial[0].num_connections++; //root only has connections to children
directify(&initial[0], NULL, 0);
assert(procp == processed + n);
assert(clp == child_links + (n - 1));
free(initial); initial = NULL;
free(links); links = NULL;
//heavy-light decoposition
int num_chains = 0;
hld(&processed[0], NULL, 1, &num_chains);
//printf("made %d chains\n", num_chains);
//queries
for (int i = 0; i < q; i++) {
int t, ui, vi;
scanf("%d %d %d", &t, &ui, &vi);
if (t == 1) {
node *u = mapping[ui];
u->seg.max_val = vi;
segment_update(u->seg.parent);
}
else {
node *u = mapping[ui];
node *v = mapping[vi];
int val = path_max(u, v);
printf("%d\n", val);
}
}
return 0;
}
Copy The Code &
Try With Live Editor
#2 Code Example with C++ Programming
Code -
C++ Programming
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50010;
class node {
public :
int l, r, mx;
node *left, *right;
void update(int idx, int val) {
if(l >= r) {
mx = val;
return;
}
int mid = (l + r) / 2;
(idx <= mid ? left : right>->update(idx, val);
mx = max(left->mx, right->mx);
}
int query(int a, int b) {
if(b < l or r < a) return 0;
if(a <= l and r <= b> return mx;
return max(left->query(a, b), right->query(a, b));
}
node(int _l, int _r) :
l(_l), r(_r), mx(0), left(NULL), right(NULL) {}
};
node* init(int l, int r) {
node *p = new node(l, r);
if(l < r) {
int mid = (l + r> / 2;
p->left = init(l, mid);
p->right = init(mid+1, r);
}
return p;
}
vector<int> adj[N];
int n, q;
node* head[N];
vector<int> Path[N];
int sz[N], H[N], P[N], G[N], pos[N];
void dfs_init(int u, int p, int h) {
P[u] = p;
H[u] = h;
sz[u] = 1;
for(int v : adj[u]) {
if(v == p) {
continue;
}
dfs_init(v, u, h+1);
sz[u] += sz[v];
}
}
void dfs_HLD(int u) {
Path[u].push_back(u);
for(int i = 0;i < Path[u].size(); i++) {
int v = Path[u][i];
G[v] = u;
pos[v] = i;
for(int vv : adj[v]) {
if(vv == P[v]> continue;
if(2*sz[vv] >= sz[v]) {
Path[u].push_back(vv);
}else {
dfs_HLD(vv);
}
}
}
head[u] = init(0, Path[u].size() - 1);
}
int query(int u, int v) {
int ans = 0;
while(G[u] != G[v]) {
if(H[G[u]] < H[G[v]]) {
swap(u, v>;
}
ans = max(ans, head[G[u]]->query(0, pos[u]));
u = P[G[u]];
}
if(pos[u] > pos[v]) {
swap(u, v);
}
ans = max(ans, head[G[u]]->query(pos[u], pos[v]));
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> q;
for(int i = 0;i < n-1; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs_init(0, 0, 0);
dfs_HLD(0);
for(int i = 0;i < q; i++) {
int type;
cin >> type;
if(type == 1> {
int u, x;
cin >> u >> x;
head[G[u]]->update(pos[u], x);
}else {
int u, v;
cin >> u >> v;
cout << query(u, v) << "\n";
}
}
return 0;
}
Copy The Code &
Try With Live Editor
#3 Code Example with Java Programming
Code -
Java Programming
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
class Result {
/*
* Complete the 'solve' function below.
*
* The function is expected to return an INTEGER_ARRAY.
* The function accepts following parameters:
* 1. 2D_INTEGER_ARRAY tree
* 2. 2D_INTEGER_ARRAY queries
*/
public static List < Integer> solve(List result = new LinkedList<>();
List();
for (int i = 0; i < = edges.size(); i++) {
nodes.add(new ArrayList<>());
}
for (int i = 0; i < edges.size(); i++) {
List edge = edges.get(i);
int n1 = edge.get(0), n2 = edge.get(1);
nodes.get(n1).add(n2);
nodes.get(n2).add(n1);
}
// process root
List < List();
boolean[] visited = new boolean[nodes.size()];
for (int i = 0; i < nodes.size(); i++) {
children.add(new LinkedList<>());
}
LinkedList < Integer> stack = new LinkedList<>();
stack.push(0);
while (!stack.isEmpty()) {
Integer current = stack.pop();
visited[current] = true;
for (Integer connection : nodes.get(current)) {
if (!visited[connection]) {
stack.push(connection);
children.get(current).add(connection);
}
}
}
HeavyLightDecompositionTree tree = new HeavyLightDecompositionTree(children);
for (List < Integer> query : queries) {
int operation = query.get(0), arg1 = query.get(1), arg2 = query.get(2);
if (operation == 1) { // update value
tree.update(arg1, arg2);
} else if (operation == 2) { // print max value
int maximum = tree.max(arg1, arg2);
result.add(maximum);
}
}
return result;
}
}
class HeavyLightDecompositionTree {
private List < List segmentedPos;
private int[] values;
public HeavyLightDecompositionTree(List < List stack = new LinkedList<>();
stack.push(0);
while (!stack.isEmpty()) {
int nodeId = stack.pop();
List < Integer> connections = adj.get(nodeId);
if (!visited[nodeId]) {
visited[nodeId] = true;
depth[nodeId] = parent[nodeId] != -1 ? depth[parent[nodeId]] + 1 : 1;
ListIterator < Integer> connectionsIterator = connections.listIterator(connections.size());
while (connectionsIterator.hasPrevious()) {
Integer connection = connectionsIterator.previous();
stack.push(nodeId);
stack.push(connection);
if (connection != parent[nodeId] && parent[connection] == -1) parent[connection] = nodeId;
}
}
if (connections.size() == 0) weight[nodeId] = 1;
else {
int childrenWeight = 1, maxChildSize = 0;
for (Integer connection : connections) {
int childSize = weight[connection];
if (connection != parent[nodeId]) childrenWeight += childSize;
if (childSize > maxChildSize) {
maxChildSize = childSize;
heavy[nodeId] = connection;
}
}
weight[nodeId] = childrenWeight;
}
}
}
private void decompose(List < List stack = new LinkedList<>();
stack.push(new int[]{0,0});
while (!stack.isEmpty()) {
int[] e = stack.pop();
int v = e[0], h = e[1];
head[v] = h;
dfsPositions[v] = dfsPosition;
dfsValues[dfsPosition] = 0;
dfsPosition++;
if (heavy[v] != -1) {
stack.push(new int[]{heavy[v], h});
}
for (int c : adj.get(v)) {
if (c != parent[v] && c != heavy[v]) {
stack.add(new int[]{c, c});
}
}
}
}
public int max(int a, int b) {
int res = 0;
for (; head[a] != head[b]; b = parent[head[b]]) {
if (depth[head[a]] > depth[head[b]]) {
// swap(a, b);
int aux = a;
a = b;
b = aux;
}
int cur_heavy_path_max = segmentedPos.max(dfsPositions[head[b]], dfsPositions[b]);
res = Math.max(res, cur_heavy_path_max);
}
if (depth[a] > depth[b]) {
// swap(a, b);
int aux = a;
a = b;
b = aux;
}
int last_heavy_path_max = segmentedPos.max(dfsPositions[a], dfsPositions[b]);
res = Math.max(res, last_heavy_path_max);
return res;
}
public void update(int index, int value) {
//values[index] = value;
int dfsPosition = dfsPositions[index];
dfsValues[dfsPosition] = value;
segmentedPos.update(dfsPosition, value);
}
}
class SegmentTreeMax < T extends Comparable> {
private T[] values;
private ArrayList tree;
public SegmentTreeMax(T[] values) {
this.values = values;
this.tree = new ArrayList < T>(this.values.length * 4);
for (int i = 0; i < this.values.length * 4; i++) {
this.tree.add(null);
}
build(1, 0, values.length - 1);
}
private void build(int v, int tl, int tr) {
if (tl == tr) {
tree.set(v, values[tl]);
} else {
int tm = (tl + tr) / 2;
build(v * 2, tl, tm);
build(v * 2 + 1, tm + 1, tr);
T left = tree.get(v * 2),
right = tree.get(v * 2 + 1);
tree.set(v, left.compareTo(right) > 0 ? left : right);
}
}
public T max(int l, int r) {
return max(1, 0, values.length - 1, l, r);
}
public void update(int pos, T value) {
update(1, 0, values.length - 1, pos, value);
}
private void update(int v, int tl, int tr, int pos, T value) {
if (tl == tr) {
tree.set(v, value);
} else {
int tm = (tl + tr) / 2;
if (pos < = tm) {
update(v * 2, tl, tm, pos, value);
}
else {
update(v * 2 + 1, tm + 1, tr, pos, value);
}
T max1 = tree.get(v * 2),
max2 = tree.get(v * 2 + 1);
if (max1 == null && max2 == null) throw new NullPointerException("Cannot perform update with 2 nulls");
else if (max1 == null) tree.set(v, max2);
else if (max2 == null) tree.set(v, max1);
else tree.set(v, max1.compareTo(max2) > 0 ? max1 : max2);
}
}
private T max(int v, int tl, int tr, int l, int r) {
if (l > r)
return null; // return value that will not affect the reduction
if (l == tl && r == tr) {
return tree.get(v);
}
int tm = (tl + tr) / 2;
T max1 = max(v*2, tl, tm, l, Math.min(r, tm)),
max2 = max(v*2+1, tm+1, tr, Math.max(l, tm+1), r);
if (max1 == null && max2 == null) throw new NullPointerException("Cannot get a maximum value from 2 nulls");
else if (max1 == null) return max2;
else if (max2 == null) return max1;
else return max1.compareTo(max2) > 0 ? max1 : max2;
}
}
public class Solution {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] firstMultipleInput = bufferedReader.readLine().replaceAll("\\s+$", "").split(" ");
int n = Integer.parseInt(firstMultipleInput[0]);
int q = Integer.parseInt(firstMultipleInput[1]);
List < List();
IntStream.range(0, n - 1).forEach(i -> {
try {
tree.add(
Stream.of(bufferedReader.readLine().replaceAll("\\s+$", "").split(" "))
.map(Integer::parseInt)
.collect(toList())
);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
List < List();
IntStream.range(0, q).forEach(i -> {
try {
queries.add(
Stream.of(bufferedReader.readLine().replaceAll("\\s+$", "").split(" "))
.map(Integer::parseInt)
.collect(toList())
);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
List < Integer> result = Result.solve(tree, queries);
bufferedWriter.write(
result.stream()
.map(Object::toString)
.collect(joining("\n"))
+ "\n"
);
bufferedReader.close();
bufferedWriter.close();
}
}
Copy The Code &
Try With Live Editor
#4 Code Example with Python Programming
Code -
Python Programming
#!/bin/python3
import math
import os
import random
import re
import sys
#
# Complete the 'solve' function below.
#
# The function is expected to return an INTEGER_ARRAY.
# The function accepts following parameters:
# 1. 2D_INTEGER_ARRAY tree
# 2. 2D_INTEGER_ARRAY queries
#
from collections import OrderedDict
def segtree_init(ary):
ary = list(ary)
seg = [ary]
while len(ary) > 1:
if len(ary) & 1: ary.append(0)
ary = [max(ary[i],ary[i+1]) for i in range(0,len(ary),2)]
seg.append(ary)
return seg
def segtree_set(seg, i, x):
ary = seg[0]
ary[i] = x
for j in range(1, len(seg)):
x = max(ary[i], ary[i^1])
ary = seg[j]
i >>= 1
ary[i] = x
def segtree_max(seg, lo, hi):
m = 0
j = 0
while lo < hi:
ary = seg[j]
if lo & 1:
x = ary[lo]
if x > m: m = x
lo += 1
if hi & 1:
hi -= 1
x = ary[hi]
if x > m: m = x
lo >>= 1
hi >>= 1
j += 1
return m
class heavy_light_node:
def __init__(self, segtree):
self.parent = None
self.pos = -1
self.segtree = segtree
def build_tree(i, edges, location):
children = []
members = [i]
ed = edges[i]
while ed:
for j in range(1,len(ed)):
child = build_tree(ed[j], edges, location)
child.pos = len(members) - 1
children.append(child)
i = ed[0]
members.append(i)
ed = edges[i]
node = heavy_light_node(segtree_init(0 for j in members))
for child in children:
child.parent = node
for j in range(len(members)):
location[members[j]] = (node, j)
return node
def read_tree(N):
edges = [[] for i in range(N)]
for i in range(N-1):
x, y = map(int, input().split())
edges[x].append(y)
edges[y].append(x)
size = [0] * N
active = [0]
while active:
i = active[-1]
if size[i] == 0:
size[i] = 1
for j in edges[i]:
edges[j].remove(i)
active.append(j)
else:
active.pop()
edges[i].sort(key=lambda j: -size[j])
size[i] = 1 + sum(size[j] for j in edges[i])
location = [None] * N
build_tree(0, edges, location)
return location
def root_path(i, location):
loc = location[i]
path = [ loc ]
loc = loc[0]
while loc.parent != None:
path.append((loc.parent, loc.pos))
loc = loc.parent
path.reverse()
return path
def max_weight(x, y):
px = root_path(x, location)
py = root_path(y, location)
m = 1
stop = min(len(px), len(py))
while m < stop and px[m][0] == py[m][0]: m += 1
loc, a = px[m-1]
b = py[m-1][1]
if a > b: a, b = b, a
w = segtree_max(loc.segtree, a, b+1)
for j in range(m, len(px)):
loc, i = px[j]
x = segtree_max(loc.segtree, 0, i+1)
if x > w: w = x
for j in range(m, len(py)):
loc, i = py[j]
x = segtree_max(loc.segtree, 0, i+1)
if x > w: w = x
return w
N, Q = map(int, input().split())
location = read_tree(N)
for i in range(Q):
t, x, y = map(int, input().split())
if t == 1:
loc, i = location[x]
segtree_set(loc.segtree, i, y)
elif t == 2:
print(max_weight(x, y))
Copy The Code &
Try With Live Editor