#include using namespace std; using ll = long long; constexpr int inf = 1e9; struct edge { int from, to; }; using graph = vector>; void add_edge(graph& g, int from, int to) { g[from].push_back(edge{from, to}); g[to].push_back(edge{to, from}); } class heavy_light_decomposition { public: heavy_light_decomposition(int n_) : n(n_), g(n), par(n), head(n), in(n), out(n) {} heavy_light_decomposition(std::vector> const& g_) : n(g_.size()), g(g_), par(n), head(n), in(n), out(n) {} void add_edge(int u, int v) { g[u].push_back(v); g[v].push_back(u); } void build(int rt = 0) { dfs1(rt); head[rt] = rt; dfs2(rt); } int get_id(int v) const { return in[v]; } void path_query(int u, int v, std::function f) { // vertex while(true) { if(in[u] > in[v]) std::swap(u, v); f(std::max(in[head[v]], in[u]), in[v] + 1); if(head[u] == head[v]) return; v = par[head[v]]; } } void edge_path_query(int u, int v, std::function f) { while(true) { if(in[u] > in[v]) std::swap(u, v); if(head[u] != head[v]) { f(std::max(in[head[v]], in[u]), in[v] + 1); v = par[head[v]]; } else { if(u != v) f(in[u] + 1, in[v] + 1); break; } } } void subtree_query(int v, std::function f) { f(in[v], out[v]); } int get_lca(int u, int v) const { while(true) { if(in[u] > in[v]) std::swap(u, v); if(head[u] == head[v]) return u; v = par[head[v]]; } } private: void dfs1(int rt) { std::vector sz(n, 1), iter(n); std::vector> st = {{rt, -1}}; st.reserve(n); while(!st.empty()) { const int v = st.back().first; if(iter[v] < (int)g[v].size()) { if(g[v][iter[v]] != st.back().second) { st.emplace_back(g[v][iter[v]], v); } ++iter[v]; continue; } par[v] = st.back().second; if(par[v] != -1) { const int pidx = std::find(std::begin(g[v]), std::end(g[v]), par[v]) - std::begin(g[v]); std::swap(g[v].back(), g[v][pidx]); g[v].pop_back(); } for(auto& u : g[v]) { sz[v] += sz[u]; if(sz[u] > sz[g[v].front()]) std::swap(u, g[v].front()); } st.pop_back(); } } void dfs2(int rt) { int it = 0; std::vector st = {rt}, iter(n); st.reserve(n); while(!st.empty()) { const int v = st.back(); if(!iter[v]) in[v] = it++; // first visit if(iter[v] < (int)g[v].size()) { int u = g[v][iter[v]]; head[u] = iter[v] ? u : head[v]; st.push_back(u); ++iter[v]; continue; } out[v] = it; st.pop_back(); } } private: const int n; std::vector> g; std::vector par, head, in, out; }; class lowlink { // multiple edges ok public: lowlink(graph const& g) : ord(g.size()), low(g.size()) { const int n = g.size(); std::vector visited(n); int cnt = 0; for(int i = 0; i < n; ++i) { if(!visited[i]) { dfs(g, i, -1, cnt, visited); } } } std::vector get_articulation_points() const { return articulation_points; } std::vector get_bridges() const { return bridges; } bool is_bridge(int u, int v) const { if(ord[u] > ord[v]) std::swap(u, v); return ord[u] < low[v]; } private: void dfs(graph const& g, int v, int prev, int& cnt, std::vector& visited) { visited[v] = true; low[v] = ord[v] = cnt++; bool is_articulation = false; int cnt2 = 0, pcnt = 0; for(auto& e : g[v]) { if((e.to != prev || (e.to == prev && pcnt == 1)) && visited[e.to]) { low[v] = min(low[v], ord[e.to]); } else if(!visited[e.to]) { cnt2++; dfs(g, e.to, v, cnt, visited); low[v] = min(low[v], low[e.to]); if(prev != -1 && ord[v] <= low[e.to]) { is_articulation = true; } if(is_bridge(v, e.to)) { bridges.push_back(edge{min(v, e.to), max(v, e.to)}); } } pcnt += e.to == prev; } is_articulation |= (prev == -1 && cnt2 > 1); if(is_articulation) articulation_points.push_back(v); } private: std::vector articulation_points; std::vector bridges; std::vector ord, low; }; class biconnected_component { public: biconnected_component(graph const& g_) : g(g_), helper(g_), belongs_(g_.size(), -1) { for(auto&& b : helper.get_bridges()) { add_component(b.from), add_component(b.to); } add_component(0); } graph build() { // if not connected, result is forest graph res(cmps.size()); auto bridges = helper.get_bridges(); for(auto& b : bridges) { add_edge(res, belongs(b.from), belongs(b.to)); } return res; } int belongs(int u) const { return belongs_[u]; } private: void fill_component(int c, int u) { cmps[c].emplace_back(u); belongs_[u] = c; for(auto& e : g[u]) { if(belongs_[e.to] >= 0 || helper.is_bridge(u, e.to)) continue; fill_component(c, e.to); } } void add_component(int u) { if(belongs_[u] >= 0) return; cmps.emplace_back(); fill_component(cmps.size() - 1, u); } private: graph g; lowlink helper; std::vector belongs_; std::vector> cmps; }; template class segment_tree { using T = typename Monoid::type; public: segment_tree(std::vector const& init) : sz(init.size()), n(expand(init.size())) { dat.assign(n * 2, Monoid::id()); std::copy(begin(init), end(init), begin(dat) + n); for(int i = n - 1; i >= 0; --i) { dat[i] = Monoid::op(dat[i * 2], dat[i * 2 + 1]); } } segment_tree(int const n_, T const& init = Monoid::id()) : segment_tree(std::vector(n_, init)) {} void update(int p, T val) { assert(0 <= p && p < sz); dat[p += n] = val; while(p /= 2) { dat[p] = Monoid::op(dat[p * 2], dat[p * 2 + 1]); } } // [l, r) T query(int l, int r) const { assert(0 <= l && l <= r && r <= sz); l += n, r += n; T res1 = Monoid::id(), res2 = Monoid::id(); while(l != r) { if(l & 1) res1 = Monoid::op(res1, dat[l++]); if(r & 1) res2 = Monoid::op(dat[--r], res2); l /= 2, r /= 2; } return Monoid::op(res1, res2); } private: int expand(int n_) const { assert(n_ >= 1); return n_ == 1 ? n_ : expand((n_ + 1) / 2) * 2; } private: const int sz, n; std::vector dat; }; struct RMQ { using type = int; static type id() { return -1; } static type op(type const& l, type const& r) { return std::max(l, r); } }; int main() { ios_base::sync_with_stdio(false); cin.tie(0); int n, m, q; cin >> n >> m >> q; graph g(n); vector a(m), b(m); for(int i = 0; i < m; ++i) { cin >> a[i] >> b[i]; a[i]--, b[i]--; add_edge(g, a[i], b[i]); } biconnected_component bicc(g); g = bicc.build(); n = g.size(); heavy_light_decomposition hld(n); for(auto& es : g) { for(auto& e : es) { if(e.from < e.to) hld.add_edge(e.from, e.to); } } hld.build(); segment_tree seg(n); vector> qs(n); map w_to_v; for(int i = 0; i < q; ++i) { int qtype; cin >> qtype; if(qtype == 1) { int u, w; cin >> u >> w; u = hld.get_id(bicc.belongs(u - 1)); w_to_v[w] = u; qs[u].push(w); seg.update(u, qs[u].top()); } else { int s, t; cin >> s >> t; s = bicc.belongs(s - 1), t = bicc.belongs(t - 1); int ans = -1; hld.path_query(s, t, [&] (int l, int r) { ans = max(ans, seg.query(l, r)); }); cout << ans << endl; if(ans == -1) continue; const int v = w_to_v[ans]; qs[v].pop(); seg.update(v, qs[v].empty() ? -1 : qs[v].top()); } } }