#include using namespace std; using ll = long long; using P = pair; template constexpr T INF = numeric_limits::max() / 10; struct Graph { Graph(const int v) : V{v} { edge.resize(v); rev_edge.resize(v); } void addEdge(const int from, const int to) { edge[from].push_back(to); rev_edge[to].push_back(from); } vector> edge; vector> rev_edge; const int V; }; class BiconnectedComponent { public: BiconnectedComponent(const Graph& g_) : num{0}, comp_num{0}, size{g_.V}, bridge(0), ord(size, -1), low(size, 0), comp(size, -1) { for (int i = 0; i < size; i++) { if (ord[i] == -1) { bridge_dfs(g_, i); } } for (int i = 0; i < size; i++) { if (comp[i] >= 0) { continue; } comp_dfs(g_, i, comp_num); comp_num++; } } Graph toTree() const { Graph tree(comp_num); for (const auto& p : bridge) { tree.addEdge(comp[p.first], comp[p.second]); tree.addEdge(comp[p.second], comp[p.first]); } return tree; } const vector>& getEdge() const { return bridge; } bool isBridge(const int i, const int j) const { return (ord[i] < ord[j]) ? ord[i] < low[j] : ord[j] < low[i]; } const vector& getComp() const { return comp; } const vector>& getBridge() const { return bridge; } private: void bridge_dfs(const Graph& g_, const int s, const int par = -1) { ord[s] = num; low[s] = num; num++; for (const int to : g_.edge[s]) { if (to == par) { continue; } if (ord[to] >= 0) { low[s] = min(low[s], ord[to]); } else { bridge_dfs(g_, to, s); low[s] = min(low[s], low[to]); if (isBridge(s, to)) { bridge.push_back(make_pair(s, to)); } } } } void comp_dfs(const Graph& g_, const int s, const int c) { comp[s] = c; for (const int to : g_.edge[s]) { if ((comp[to] == -1) and (not isBridge(s, to))) { comp_dfs(g_, to, c); } } } int num; int comp_num; const int size; vector> bridge; vector ord; vector low; vector comp; }; class HeavyLightDecomposition { private: using P = pair; public: HeavyLightDecomposition(const Graph& g, const int root = 0) : N(g.V), sub(N, 0), prev(N, -1), next(N, -1), depth(N, 0), head{root}, index(N, {-1, 0}), chains(0) { dfs(g, root, 0); chains.resize(head.size()); for (int i = 0; i < head.size(); i++) { int pos = head[i]; int v = -1; for (int ind = 0; pos != -1; ind++) { v = pos; chains[i].push_back(pos); index[pos] = {i, ind}; pos = next[pos]; } tail.push_back(v); } } P getIndex(const int v) const { return index[v]; } int getChainIndex(const int v) const { return index[v].first; } int getPosition(const int v) const { return index[v].second; } int getHead(const int v) const { return head[index[v].first]; } int getTail(const int v) const { return tail[index[v].first]; } const vector& getDepth() const { return depth; } int prevChainNode(const int v) const { return prev[getHead(v)]; } P prevChainIndex(const int v) const { return getIndex(prevChainNode(v)); } int getLCA(int u, int v) const { while (getChainIndex(u) != getChainIndex(v)) { if (depth[getHead(u)] > depth[getHead(v)]) { u = prevChainNode(u); } else { v = prevChainNode(v); } } return depth[u] < depth[v] ? u : v; } const vector>& getChains() const { return chains; } int prevChain(const int v) const { return prev[head[index[v].first]]; } const int N; private: int dfs(const Graph& g, const int s, const int d) { sub[s] = 1; depth[s] = d; vector

child; for (const int to : g.edge[s]) { if (sub[to] == 0) { prev[to] = s; const int s = dfs(g, to, d + 1); sub[s] += s; child.push_back({s, to}); } } sort(child.begin(), child.end(), greater

{}); if (child.size()) { next[s] = child[0].second; for (int i = 1; i < child.size(); i++) { head.push_back(child[i].second); } } return sub[s]; } vector sub; vector prev; vector next; vector tail; vector depth; vector head; vector

index; vector> chains; }; template class FenwickTree { public: using BaseMonoid = Monoid; using T = typename Monoid::T; FenwickTree(const int n) : data_num(n), size(1 << (1 + __lg(2 * data_num - 1))), half(size >> 1), value(size, Monoid::identity()) { assert(n > 0); } FenwickTree(const std::vector& val) : data_num(val.size()), size(1 << (1 + __lg(2 * data_num - 1))), half(size >> 1), value(size) { for (int data = 0; data < half; data++) { if (data < data_num) { value[data + half] = val[data]; } else { value[data + half] = Monoid::identity(); } } for (int node = half - 1; node >= 1; node--) { value[node] = acc(value[2 * node], value[2 * node + 1]); } } T get(const int a) const { assert(0 <= a and a < data_num); return accumulate(a, a + 1); } void set(const int a, const T& val) { assert(0 <= a and a < data_num); const int node = a + half; value[node] = val; for (int i = node / 2; i > 0; i /= 2) { value[i] = acc(value[2 * i], value[2 * i + 1]); } } T accumulate(const int a, const int b) const // Accumulate (a,b] { assert(0 <= a and a < b and b <= data_num); return accumulateRec(1, 0, half, a, b); } int query(const int a, const int b) const // query (a,b] { assert(0 <= a and a < b and b <= data_num); return queryRec(1, 0, half, a, b); } private: T accumulateRec(const int range_index, const int range_left, const int range_right, const int op_left, const int op_right) const { if (op_left <= range_left and range_right <= op_right) { return value[range_index]; } else if (range_right <= op_left or op_right <= range_left) { return Monoid::identity(); } else { return acc(accumulateRec(2 * range_index, range_left, (range_left + range_right) / 2, op_left, op_right), accumulateRec(2 * range_index + 1, (range_left + range_right) / 2, range_right, op_left, op_right)); } } int queryRec(const int range_index, const int range_left, const int range_right, const int op_left, const int op_right) const { if (op_left <= range_left and range_right <= op_right) { return value[range_index]; } else if (range_right <= op_left or op_right <= range_left) { return Monoid::identity(); } else { return queryRec(2 * range_index, range_left, (range_left + range_right) / 2, op_left, op_right) + queryRec(2 * range_index + 1, (range_left + range_right) / 2, range_right, op_left, op_right); } } const int data_num; // Num of valid data on leaves. const int size; const int half; vector value; // Tree for value(length: size) const Monoid acc{}; }; struct Max { using T = pair; T operator()(const T& a, const T& b) const { return max(a, b); } static constexpr T identity() { return make_pair(-INF, -1); } }; int main() { cin.tie(0); ios::sync_with_stdio(false); int N, M, Q; cin >> N >> M >> Q; Graph g_(N); for (int i = 0; i < M; i++) { int a, b; cin >> a >> b; a--, b--; g_.addEdge(a, b); g_.addEdge(b, a); } BiconnectedComponent bic(g_); const int comp = bic.getComp().size(); auto g = bic.toTree(); vector> value(comp); HeavyLightDecomposition hld(g); vector> segs; for (const auto& chain : hld.getChains()) { vector> v(chain.size()); for (int i = 0; i < chain.size(); i++) { v[i] = {-1, i}; } segs.push_back(FenwickTree{v}); } for (int q = 0; q < Q; q++) { int t; cin >> t; if (t == 1) { int u; ll v; cin >> u >> v; u--; const int comp = bic.getComp()[u]; value[comp].push(v); const P chind = hld.getIndex(comp); segs[chind.first].set(chind.second, {value[comp].top(), chind.second}); } else { int s, t; cin >> s >> t; s--, t--; s = bic.getComp()[s]; t = bic.getComp()[t]; const int lca = hld.getLCA(s, t); const P lca_ch = hld.getIndex(lca); int pos[2] = {s, t}; pair ans{-1, -1}; int maxchain = -1; for (int i = 0; i < 2; i++) { while (true) { const P index = hld.getIndex(pos[i]); const int head = (lca_ch.first == index.first ? lca_ch.second : 0); const auto val = segs[index.first].accumulate(head, index.second + 1); if (ans < val) { ans = val; maxchain = index.first; } if (lca_ch.first == index.first) { break; } pos[i] = hld.prevChainNode(pos[i]); } } if (ans.first > 0) { const int node = hld.getChains()[maxchain][ans.second]; value[node].pop(); const ll val = (value[node].empty() ? -1 : value[node].top()); segs[maxchain].set(ans.second, make_pair(val, ans.second)); } cout << ans.first << endl; } } return 0; }