Cod sursa(job #2950870)

Utilizator redstonegamer22Andrei Ion redstonegamer22 Data 4 decembrie 2022 19:48:23
Problema Heavy Path Decomposition Scor 20
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 7.95 kb
#pragma GCC optimize("Ofast")
#define NDEBUG
#include <bits/stdc++.h>

using namespace std;

#ifndef LOCAL
ifstream in("heavypath.in");
ofstream out("heavypath.out");
#define cin in
#define cout out
#endif // LOCAL

const int NMAX = 1e5 + 8;

#include <vector>
#include <cstdint>
#include <cassert>
#include <string>

namespace ja::AdvancedDataStructures::SegmentTree {
	#ifndef jassert
		#define jassert(expr, msg, ...) assert(expr && msg)
	#endif // jassert
	#ifndef jlog
		#define jlog(...) ((void)0)
	#endif // jlog

	template < typename DATA, typename OPS >
	class PointUpdateRangeQuery {
	private:
		std::vector<DATA> data;
		int32_t offset;
		OPS op{};

		void _set(int32_t poz, DATA val) {
			data[poz + offset] = val;
			for(poz = (poz + offset) / 2; poz > 0; poz /= 2)
				data[poz] = op.sum(data[2 * poz], data[2 * poz + 1]);
		}

		DATA _get(int32_t poz) {
			return data[poz + offset];
		}

		void _update(int32_t poz, DATA val) {
			data[poz + offset] = op.sum(data[poz + offset], val);
			for(poz = (poz + offset) / 2; poz > 0; poz /= 2)
				data[poz] = op.sum(data[2 * poz], data[2 * poz + 1]);
		}

		DATA _query(int32_t l, int32_t r) {
			DATA l_ret = op.zero(), r_ret = op.zero();
			l = l + offset; r = r + offset;

			while(l < r) {
				if(l & 1) { l_ret = op.sum(l_ret, data[l]); l += 1; } // l is a right child, include it
				if(r & 1) { r -= 1; r_ret = op.sum(data[r], r_ret); } // r is a right child, exclude it
				l /= 2; r /= 2;
			}

			return op.sum(l_ret, r_ret);
		}

	public:
		PointUpdateRangeQuery(int32_t n) {
		    offset = 1;
			while(offset < n) offset *= 2;
			data.resize(2 * offset, op.zero());
		};

		void set(int32_t position, DATA value) {
			jassert(0 <= position && position < offset, "position is out of bounds", position, offset);
			_set(position, value);
		}

		DATA get(int32_t position) {
			jassert(0 <= position && position < offset, "position is out of bounds", position, offset);
			return _get(position);
		}

		void update(int32_t position, DATA change) {
			jassert(0 <= position && position < offset, "position is out of bounds", position, offset);
			_update(position, change);
		}

		DATA query(int32_t left, int32_t right) {
			jassert(0 <= left && left <= right && right < offset, "left and right are not correct", left, right, offset);
			return _query(left, right + 1);
		}

		std::string to_string() {
			std::string ret = "[ ";
			for(int i = 1; i < data.size(); i++) {
				if((i & (i - 1)) == 0) { // i is the first node on its level
					if(i != 1) { // doesn't matter for the root
						ret = ret + " | ";
					}
				}
				ret = ret + std::to_string(i) + ": " + std::to_string(data[i]);
				if(((i + 1) & i) != 0) { // i is not the last node on its level
					ret = ret + ", ";
				}
			}
			ret = ret + " ]";
			return ret;
		}
	};

	template < typename DATA, typename OPS >
	class RangeUpdatePointQuery {
	private:
		std::vector<DATA> data;
		int32_t offset;

		OPS op{};

		DATA _query(int32_t position) {
			DATA ret = op.zero();
			for(int i = position + offset; i > 0; i /= 2)
				ret = op.sum(ret, data[i]);
			return ret;
		}

		void _right_update(int32_t l, int32_t r, DATA change) { // Update with change on the right
			l = l + offset; r = r + offset;
			while(l < r) {
				if(l & 1) { data[l] = op.sum(data[l], change); l += 1; } // l is a right child, update it
				if(r & 1) { r -= 1; data[r] = op.sum(data[r], change); } // r is a right child, update its sibling

				l /= 2; r /= 2;
			}
		}

		void _left_update(int32_t l, int32_t r, DATA change) { // Updte with change on the left
			l = l + offset; r = r + offset;
			while(l < r) {
				if(l & 1) { data[l] = op.sum(change, data[l]); l += 1; } // l is a right child, update it
				if(r & 1) { r -= 1; data[r] = op.sum(change, data[r]); } // r is a right child, update its sibling

				l /= 2; r /= 2;
			}
		}

	public:
		RangeUpdatePointQuery(int32_t n) {
			offset = 1;
			while(offset < n) offset *= 2;
			data.resize(2 * offset, op.zero());
		}

		DATA query(int32_t position) {
			jassert(0 <= position && position < offset, "position is out of bounds", position, offset);
			return _query(position);
		}

		void right_update(int32_t left, int32_t right, DATA change) {
			jassert(0 <= left && left <= right && right < offset, "left and right are not correct", left, right, offset);
			_right_update(left, right + 1, change);
		}
		void left_update(int32_t left, int32_t right, DATA change) {
			jassert(0 <= left && left <= right && right < offset, "left and right are not correct", left, right, offset);
			_left_update(left, right + 1, change);
		}

		void update(int32_t left, int32_t right, DATA change) { // Updates are on the right by default
			right_update(left, right, change);
		}

		std::string to_string() {
			std::string ret_data = "[ ";
			for(int i = 1; i < data.size(); i++) {
				if((i & (i - 1)) == 0) { // i is the first node on its level
					if(i != 1) { // doesn't matter for the root
						ret_data = ret_data + " | ";
					}
				}
				ret_data = ret_data + std::to_string(i) + ": " + std::to_string(data[i]);
				if(((i + 1) & i) != 0) { // i is not the last node on its level
					ret_data = ret_data + ", ";
				}
			}
			ret_data = ret_data + " ]";

			std::string ret_value = "[ ";
			for(int i = 0; i < offset; i++) {
				if(i != 0) ret_value += ", ";
				ret_value += std::to_string(query(i));
			}
			ret_value += " ]";


			return ret_data + " -> " + ret_value;
		}
	};
};
struct st_ops {
    inline int zero() const { return -1e9; }
    inline int sum(int a, int b) { return a > b ? a : b; }
};
ja::AdvancedDataStructures::SegmentTree::PointUpdateRangeQuery<int, st_ops> st(NMAX);

vector<int> g[NMAX], liniarize;
int vals[NMAX];
int heavy[NMAX], stsz[NMAX], depth[NMAX], immpar[NMAX];
int inv_liniarize[NMAX], chain_head[NMAX];

void dfs(int node, int parent) {
    // cerr << "DFS " << node << " " << parent << endl;

    immpar[node] = parent;
    stsz[node] = 1; depth[node] = depth[parent] + 1;
    heavy[node] = -1;
    for(auto vec : g[node]) {
        if(vec == parent) continue;
        dfs(vec, node);

        if(heavy[node] == -1 || stsz[heavy[node]] < stsz[vec]) {
            heavy[node] = vec;
        }
    }
}

void lin(int node, int head) {
    // cerr << "LIN " << node << " " << head << endl;

    chain_head[node] = head;
    inv_liniarize[node] = liniarize.size();
    liniarize.push_back(node);

    if(heavy[node] != -1) lin(heavy[node], head);
    for(auto vec : g[node]) {
        if(vec == immpar[node] || vec == heavy[node]) continue;
        lin(vec, vec);
    }
}

void update(int node, int value) {
    st.set(inv_liniarize[node], value);
}

int query(int a, int b) {
    // cerr << "Query " << a << " " << b << endl;
    // this_thread::sleep_for(1s);
    if(chain_head[a] == chain_head[b]) {
        if(depth[a] > depth[b]) swap(a, b);
        return st.query(inv_liniarize[a], inv_liniarize[b]);
    }

    if(depth[chain_head[a]] < depth[chain_head[b]]) swap(a, b);

    // cerr << "Continuing " << a << " " << b << endl;
    // cerr << "with " << immpar[chain_head[a]] << " " << b << endl;

    return std::max(
        st.query(inv_liniarize[chain_head[a]], inv_liniarize[a]),
        query(immpar[chain_head[a]], b)
    );
}

const int root = 1;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int n, m; cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> vals[i];

    for(int i = 0; i < n - 1; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }

    dfs(root, 0); lin(root, root);

    for(int i = 1; i <= n; i++) update(i, vals[i]);

    for(int i = 0; i < m; i++) {
        int t, x, y; cin >> t >> x >> y;
        if(t == 0) {
            update(x, y);
        }
        else {
            cout << query(x, y) << '\n';
        }
    }
}