Mai intai trebuie sa te autentifici.
Cod sursa(job #2231865)
Utilizator | Data | 16 august 2018 12:40:23 | |
---|---|---|---|
Problema | Heavy Path Decomposition | Scor | 20 |
Compilator | java | Status | done |
Runda | Arhiva educationala | Marime | 9.99 kb |
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("heavypath.in");
OutputStream outputStream = new FileOutputStream("heavypath.out");
try (InputReader inputReader = new InputReader(inputStream);
PrintWriter printWriter = new PrintWriter(outputStream)) {
int N = inputReader.nextInt();
int M = inputReader.nextInt();
int[] valuesPerNode = new int[N + 1];
for (int node = 1; node <= N; node++) {
valuesPerNode[node] = inputReader.nextInt();
}
Graph graph = new Graph(N);
for (int edgeNo = 1; edgeNo < N; edgeNo++) {
graph.addEdge(inputReader.nextInt(), inputReader.nextInt());
}
HeavyPathDecomp heavyPathDecomp = new HeavyPathDecomp(graph, valuesPerNode);
int type, x, y;
for (int queryNo = 1; queryNo <= M; queryNo++) {
type = inputReader.nextInt();
x = inputReader.nextInt();
y = inputReader.nextInt();
switch (type) {
case 0:
heavyPathDecomp.setValue(x, y);
break;
case 1:
printWriter.println(heavyPathDecomp.getMaxOnPath(x, y));
break;
default:
throw new RuntimeException("Unexpected type: " + type);
}
}
}
}
static class HeavyPathDecomp {
private Graph graph;
private List<List<Integer>> paths;
private int[] nodePathIdx;
private int[] nodePosInPath;
private int[] childrenPerNode;
private int[] parent;
private int[] depth;
private SegmentTree[] segmentTrees;
public HeavyPathDecomp(Graph graph, int[] costPerNode) {
this.graph = graph;
this.nodePathIdx = new int[graph.getNumberOfNodes() + 1];
this.nodePosInPath = new int[graph.getNumberOfNodes() + 1];
this.parent = new int[graph.getNumberOfNodes() + 1];
this.depth = new int[graph.getNumberOfNodes() + 1];
Arrays.fill(depth, 0);
BitSet visited = new BitSet(graph.getNumberOfNodes() + 1);
childrenPerNode = new int[graph.getNumberOfNodes() + 1];
computeChildrenPerNode(1, visited);
paths = new ArrayList<>();
List<Integer> newPath = new ArrayList<>();
paths.add(newPath);
visited.clear();
decomposeGraph(1, visited, newPath);
segmentTrees = new SegmentTree[paths.size()];
for (int pathIdx = 0; pathIdx < paths.size(); pathIdx++) {
List<Integer> currPath = paths.get(pathIdx);
int[] valuesPath = new int[currPath.size() + 1];
for (int pathEntryIdx = 0; pathEntryIdx < currPath.size(); pathEntryIdx++) {
valuesPath[pathEntryIdx + 1] = costPerNode[currPath.get(pathEntryIdx)];
}
segmentTrees[pathIdx] = new SegmentTree(currPath.size(), valuesPath);
}
}
public void setValue(int node, int newValue) {
int pathIdx = nodePathIdx[node];
int posInPath = nodePosInPath[node];
segmentTrees[pathIdx].setValue(posInPath + 1, newValue);
}
private int getStartNodeOnPath(int node) {
return paths.get(nodePathIdx[node]).get(0);
}
private int getMaxOnPath(int node1, int node2) {
int ans = Integer.MIN_VALUE;
int startNodePath1, startNodePath2;
while (nodePathIdx[node1] != nodePathIdx[node2]) {
startNodePath1 = getStartNodeOnPath(node1);
startNodePath2 = getStartNodeOnPath(node2);
if (depth[startNodePath1] > depth[startNodePath2]) {
ans = Math.max(ans,
segmentTrees[nodePathIdx[node1]].getMax(1, nodePosInPath[node1] + 1));
node1 = parent[startNodePath1];
} else {
ans = Math.max(ans,
segmentTrees[nodePathIdx[node2]].getMax(1, nodePosInPath[node2] + 1));
node2 = parent[startNodePath2];
}
}
if (depth[node1] < depth[node2]) {
ans = Math.max(ans,
segmentTrees[nodePathIdx[node1]].getMax(nodePosInPath[node1] + 1, nodePosInPath[node2] + 1));
} else {
ans = Math.max(ans,
segmentTrees[nodePathIdx[node1]].getMax(nodePosInPath[node2] + 1, nodePosInPath[node1] + 1));
}
return ans;
}
private void computeChildrenPerNode(int node, BitSet visited) {
childrenPerNode[node] = 1;
visited.set(node);
for (int neighbour : graph.getEdges(node)) {
if (!visited.get(neighbour)) {
parent[neighbour] = node;
depth[neighbour] = depth[node] + 1;
computeChildrenPerNode(neighbour, visited);
childrenPerNode[node] += node;
}
}
}
private void decomposeGraph(int node, BitSet visited, List<Integer> currPath) {
visited.set(node);
currPath.add(node);
nodePathIdx[node] = paths.size() - 1;
nodePosInPath[node] = currPath.size() - 1;
int bestChild = -1, bestChildWeight = -1;
for (int child : graph.getEdges(node)) {
if (!visited.get(child)) {
if (childrenPerNode[child] > bestChildWeight) {
bestChildWeight = childrenPerNode[child];
bestChild = child;
}
}
}
if (bestChild == -1) {
return;
}
decomposeGraph(bestChild, visited, currPath);
for (int child : graph.getEdges(node)) {
if (!visited.get(child)) {
if (child != bestChild) {
List<Integer> newPath = new ArrayList<>();
paths.add(newPath);
decomposeGraph(child, visited, newPath);
}
}
}
}
}
static class SegmentTree {
private int N;
private int[] maxPerNode;
public SegmentTree(int N, int[] values) {
this.N = N;
int reqSize = 1;
while (reqSize < N) {
reqSize *= 2;
}
maxPerNode = new int[reqSize * 2 + 1];
cstr(1, N, 1, values);
}
private void cstr(int left, int right, int node, int[] values) {
if (left == right) {
maxPerNode[node] = values[left];
return;
}
int mid = (left + right) / 2;
cstr(left, mid, node * 2, values);
cstr(mid + 1, right, node * 2 + 1, values);
maxPerNode[node] = Math.max(maxPerNode[node * 2], maxPerNode[node * 2 + 1]);
}
public void setValue(int pos, int value) {
setValue(1, N, 1, pos, value);
}
public int getMax(int from, int to) {
return getMax(1, N, 1, from, to);
}
private int getMax(int left, int right, int node, int from, int to) {
if (to < left || right < from) {
return Integer.MIN_VALUE;
}
if (from <= left && right <= to) {
return maxPerNode[node];
}
int mid = (left + right) / 2;
return Math.max(
getMax(left, mid, node * 2, from, to),
getMax(mid + 1, right, node * 2 + 1, from, to));
}
private void setValue(int left, int right, int node, int pos, int value) {
if (left == right) {
maxPerNode[node] = value;
return;
}
int mid = (left + right) / 2;
if (pos <= mid) {
setValue(left, mid, node * 2, pos, value);
} else {
setValue(mid + 1, right, node * 2 + 1, pos, value);
}
maxPerNode[node] = Math.max(maxPerNode[node * 2], maxPerNode[node * 2 + 1]);
}
}
static class Graph {
private int N;
private List<List<Integer>> edges;
public Graph(int N) {
this.N = N;
edges = new ArrayList<>(N + 1);
for (int node = 0; node <= N; node++) {
edges.add(new ArrayList<>());
}
}
public List<Integer> getEdges(int node) {
return edges.get(node);
}
public void addEdge(int from, int to) {
edges.get(from).add(to);
edges.get(to).add(from);
}
public int getNumberOfNodes() {
return N;
}
}
static class InputReader implements AutoCloseable {
private BufferedReader bufferedReader;
private StringTokenizer stringTokenizer;
public InputReader(InputStream inputStream) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
stringTokenizer = null;
}
private String nextToken() {
if (stringTokenizer == null || !stringTokenizer.hasMoreTokens()) {
try {
stringTokenizer = new StringTokenizer(bufferedReader.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return stringTokenizer.nextToken();
}
public int nextInt() {
return Integer.parseInt(nextToken());
}
@Override
public void close() throws IOException {
bufferedReader.close();
}
}
}