2022 International Collegiate Programming Contest, Jinan Site 部分题目简要题解
从这里开始
Problem B Torch
注意到 $a_1, b_1, a_2, b_2$ 的和不会超过 $10^6$
考虑胖先生的周期开始的时候,瘦先生的周期在时刻 $t$,距离胖先生的距离为 $x + 1$,那么胖先生的周期结束的时候,瘦先生的距离胖先生的距离大概是 $\max(x + c, 0) + 1$ 之类的东西,其中 $c$ 是一个和 $t$ 有关的常数。
这个信息不难合并。对于每个询问,相当于是若干个循环再加上一个前缀和之类的东西,然后再处理一下一个周期中零散的情况。
时间复杂度 $O(a_1 + b_1 + n)$。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long class Input { public: #define digit(_x) ((_x) >= '0' && (_x) <= '9') template <typename T> Input& operator >> (T &u) { char x; while (~(x = getchar()) && !digit(x)); for (u = x - '0'; ~(x = getchar()) && digit(x); u = u * 10 + x - '0'); return *this; } } in; class Output { public: Output& operator << (ll u) { static char stk[30]; char *p = stk + 26; *p = 0; if (!u) *(--p) = '0'; while (u) { *(--p) = u % 10 + '0'; u /= 10; } puts(p); return *this; } } out; typedef class Data { public: ll x, y; Data(ll x = 0, ll y = 0) : x(x), y(y) { } Data operator + (Data b) { return Data(x + b.x, max(y + b.x, b.y)); } Data pow(ll p) { return p ? Data(x * p, max(y + x * (p - 1), y)) : Data(0, 0); } ll eval() { return max(x, y); } } Data; int T; int n, m; int a1, b1, a2, b2, q; vector<Data> f; ll calc_step(ll x) { return (x / m) * a2 + min(x % m, (ll) a2); } ll calc_step(ll l, ll r) { return calc_step(r) - calc_step(l - 1); } ll calc_step1(ll x) { return (x / n) * a1 + min(x % n, (ll) a1); } Data prepare(int s) { ll d = a1 - calc_step(s + 1, s + n); return Data(d, 0); } void solve() { in >> a1 >> b1 >> a2 >> b2 >> q; n = a1 + b1; m = a2 + b2; f = {Data(0, 0)}; int r = 0; do { f.push_back(prepare(r)); r = (r + n) % m; } while (r); for (int i = 1; i < (signed) f.size(); i++) { f[i] = f[i - 1] + f[i]; } ll qt, qtc, C = 1ll * n * (f.size() - 1); while (q--) { in >> qt; qtc = qt / C; int cr = (qt / n) % (f.size() - 1); r = qt % n; Data g = f.back().pow(qtc) + f[cr] + Data(min(r, a1) - calc_step(qt - r + 1, qt), 0); ll ans = calc_step1(qt) - g.eval(); out << ans; } } int main() { in >> T; while (T--) { solve(); } return 0; }
Problem C DFS Order 2
考虑一个点在 dfs 序在 $x$ 的方案数相当于它到根节点的祖先,每个把自己节点做背包。同时每个祖先处做背包还要维护一下在这个点之前选了多少棵子树。
直接通过合并前后的背包复杂度会炸,改成二元多项式除就可以了。
Code
考场上写过了,感觉还有点小胖,懒得再写一遍了。
Problem F Grid Points
你看我这老年人像是会补 0 队 ac 的题目的吗
Problem G Quick Sort
模拟一下这个过程可以发现 $p$ 的值要么是 $pivot$ 要么是 $pivot - 1$
考虑枚举两边中少的一边。找交换的时候另外一边的数的时候,考虑维护一下每个值的位置,然后把少的半边的值拉出来排序。
Code
#include <bits/stdc++.h> using namespace std; class Reader { public: #define digit(_x) ((_x) >= '0' && (_x) <= '9') Reader& operator >> (int &u) { char x; while (~(x = getchar()) && !digit(x)); for (u = x - '0'; ~(x = getchar()) && digit(x); u = u * 10 + x - '0'); return *this; } } in; const int N = 5e5 + 5; int T, n; int ans; int p[N], q[N]; void do_swap(int x, int y) { swap(p[x], p[y]); swap(q[p[x]], q[p[y]]); ans++; } void solve(int l, int r) { static vector<int> tmp; if (l >= r) return; int mid = (l + r) >> 1; int pvt = p[mid], pos = mid, mir = r; tmp.clear(); if (pvt <= mid) { for (int i = l; i <= pvt; i++) { tmp.push_back(q[i]); } sort(tmp.begin(), tmp.end(), greater<int>()); for (int i = l, j = 0; i <= pvt; i++) { if (p[i] >= pvt && i < tmp[j]) { mir = tmp[j]; do_swap(i, tmp[j++]); } } } else { for (int i = r; i >= pvt; i--) { tmp.push_back(q[i]); } sort(tmp.begin(), tmp.end()); for (int i = r, j = 0; i >= pvt; i--) { if (p[i] <= pvt && i > tmp[j]) { mir = i; do_swap(i, tmp[j++]); } } } pos = pvt - (p[pvt] > pvt || pvt == mir); // cerr << l << " " << r << " " << pos << endl; solve(l, pos); solve(pos + 1, r); } void solve() { in >> n; for (int i = 1; i <= n; i++) { in >> p[i]; q[p[i]] = i; } ans = 0; solve(1, n); printf("%d\n", ans); } int main() { in >> T; while (T--) { solve(); } return 0; }
Problem H Set of Intervals
首先特判 $n = 1$。
考虑判定一区间 $[x, y] (x < y)$ 是否可行。可以注意到如果有一个区间包含 $x$,那么在之后的合并过程中,我可以让它包含 $x$
如果一个区间包含 $x$,一个包含 $y$ ,那么显然我通过一次合并可以得到 $[x, y]$
现在考虑怎样凑出一个包含 $x$ 的区间:
- 本身存在一个区间包含 $x$
- $x$ 两侧分别至少存在 1 个区间
显然除了这两种情况,剩下都是不可能的
考虑扫描线,计算 $x = l$ 的时候可能的右端点的范围。
暴力的做法是枚举 $x$ 是怎么凑出来的,删掉这些区间,然后求剩下的区间中最小的左端点 $L$ 以及最大的右端点 $R$,可能的右端点的范围为 $[L, R]$
每次最多只会删掉 2 个区间,所以如果 $n$ 足够大,这些可能的右端点的范围的并集就是所有的 $L$ 取 min,右端点取 max(当 2 种凑 $x$ 方案使得剩下的区间只要有 1 个公共的时候,可以保证这两个 $[L, R]$ 不是相离的)。当 $n$ 足够大的时候,要么就是删掉 $1$ 个或者 $2$ 个区间求 $[L, R]$,要么就是对所有区间的求。
如果 $n$ 比较小,直接暴力就行了。
Code
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; typedef class Segment { public: int l, r; Segment(int l = 0, int r = 0) : l(l), r(r) { } bool operator < (Segment b) const { return (l ^ b.l) ? (l < b.l) : r < b.r; } } Segment; typedef class Event { public: int p; int l, r; Event(int p, int l, int r) : p(p), l(l), r(r) { } bool operator < (Event b) const { return p < b.p; } } Event; #define ll long long int T, n; multiset<int> Sl, Sr; multiset<Segment> Ssl, Ssm, Ssr; ll eval(ll l1, ll l2, ll r1, ll r2) { ll ret = 0; r1 = max(l1, r1); l2 = min(l2, r2); if (l2 < l1 || r2 < r1) return 0; // sum_{i = l1}^{l2} r2 - max(r1, i + 1) + 1 vector<ll> br {l1, l2 + 1, r1}; sort(br.begin(), br.end()); for (int i = 0; i < 2; i++) { ll l = br[i], r = br[i + 1] - 1; l = max(l, l1), r = min(r, l2); if (l > r) { continue; } ll vl = r2 - max(r1, l + 1) + 1; ll vr = r2 - max(r1, r + 1) + 1; ret += (vl + vr) * (r - l + 1) / 2; } // cerr << l1 << " " << l2 << " " << r1 << " " << r2 << " " << ret << endl; return ret; } void erase(Segment s) { Sl.erase(Sl.find(s.l)); Sr.erase(Sr.find(s.r)); } void insert(Segment s) { Sl.insert(s.l); Sr.insert(s.r); } Segment eval(Segment s) { erase(s); Segment ret (*Sl.begin(), *--Sr.end()); insert(s); return ret; } Segment eval(Segment s1, Segment s2) { erase(s1), erase(s2); Segment ret (*Sl.begin(), *--Sr.end()); insert(s1), insert(s2); return ret; } void solve() { scanf("%d", &n); Sl.clear(), Sr.clear(); Ssl.clear(), Ssm.clear(), Ssr.clear(); vector<Event> E; for (int i = 1, l, r; i <= n; i++) { scanf("%d%d", &l, &r); Sl.insert(l); Sr.insert(r); Ssr.insert(Segment(l, r)); E.emplace_back(l, l, r); E.emplace_back(r + 1, l, r); } if (n == 1) { puts("1"); return; } sort(E.begin(), E.end()); ll ans = 0; int L, R, l0 = *Sl.begin(), r0 = *--Sr.end(); auto it = E.begin(), _it = E.end(), pit = E.begin(); while (it != _it) { pit = it; L = pit->p; while (it != _it && it->p == pit->p) { Segment s (it->l, it->r); if (it->p == it->l) { Ssr.erase(Ssr.find(s)); Ssm.insert(s); } else { Ssm.erase(Ssm.find(s)); Ssl.insert(s); } it++; } if (it == _it) break; R = it->p; int szl = Ssl.size(), szm = Ssm.size(), szr = Ssr.size(); if (n < 8) { vector<Segment> vs; for (auto sm : Ssm) { vs.push_back(eval(sm)); } if (n > 2) { for (auto sl : Ssl) { for (auto sr : Ssr) { vs.push_back(eval(sl, sr)); } } } sort(vs.begin(), vs.end()); int l = -1, r = -1; for (auto s : vs) { if (s.l > r) { if (r != -1) ans += eval(L, R - 1, l, r); l = s.l, r = s.r; } else { r = max(r, s.r); } } if (r != -1) { ans += eval(L, R - 1, l, r); } } else if (szm + min(szl, szr) >= 2) { ans += eval(L, R - 1, l0, r0); } else if (szm == 1) { Segment s = eval(*Ssm.begin()); ans += eval(L, R - 1, s.l, s.r); } else if (szl == 1) { Segment s = eval(*Ssl.begin()); ans += eval(L, R - 1, s.l, s.r); } else if (szr == 1) { Segment s = eval(*Ssr.begin()); ans += eval(L, R - 1, s.l, s.r); } } printf("%lld\n", ans); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem I Shortest Path
傻逼学院负责人
假设有一条路径绕一个很大的环(环大小至少为 $3$),设上面的最小的边为 $e$,那么我可以通过较短的一条路径走到 $e$,然后在 $e$ 上左右横跳若干次,再折返回来,如果奇偶性不对那么这个环是个奇环,再把这个环绕一圈就可以了。
剩下就很傻逼了。给路径长度设一个阈值 $L$,然后求一下经过每条边,路径长度为 $L, L + 1$ 的最短路径。路径长度大于 $L$ 的时候搞个凸包就可以了。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long const int Mod = 998244353; template <typename T> void pfill(T* pst, T* ped, T val) { for ( ; pst != ped; *(pst++) = val); } typedef class Zi { public: int v; Zi() : v(0) { } Zi(int x) : v(x) { } Zi(ll x) : v(x % Mod) { } friend Zi operator + (Zi a, Zi b) { int x = a.v + b.v; return (x >= Mod) ? (x - Mod) : x; } friend Zi operator - (Zi a, Zi b) { int x = a.v - b.v; return (x < 0) ? (x + Mod) : x; } friend Zi operator * (Zi a, Zi b) { return 1ll * a.v * b.v; } Zi& operator += (Zi b) { return *this = *this + b; } } Zi; template <typename T> bool vmin(T& a, T b) { return a > b ? (a = b, true) : false; } const int N = 2020, M = 5020; typedef class Edge { public: int v, w; Edge(int v, int w) : v(v), w(w) { } } Edge; const ll llf = 2e18; int n, m, X; ll fs[N << 1][N], ft[N << 1][N]; vector<Edge> G[N]; void do_dp(int s, int T, ll f[][N]) { for (int i = 0; i <= T; i++) { for (int j = 1; j <= n; j++) { f[i][j] = llf; } } f[0][s] = 0; for (int t = 1; t <= T; t++) { for (int p = 1; p <= n; p++) { for (auto e : G[p]) { vmin(f[t][p], f[t - 1][e.v] + e.w); } } } } ll ceil(ll a, ll b) { return (a + b - 1) / b; } ll better(pair<int, ll> a, pair<int, ll> b) { int k1 = a.first, k2 = b.first; ll b1 = a.second, b2 = b.second; // k1 > k2 // k1 * t + b1 >= k2 * t + b2 // t >= (b2 - b1) / (k1 - k2) return ceil(b2 - b1, k1 - k2); } Zi sum2(ll n) { return (n * (n + 1)) >> 1; } Zi sum2(ll l, ll r) { return sum2(r) - sum2(l - 1); } pair<int, ll> stk[M << 2]; Zi solve(bool par, int T) { static int ws[M << 1]; static ll vs[M << 1], tmpw[M << 1]; int S = T - par; vector<pair<int, ll>> E; int m2 = m << 1 | 1; pfill(vs, vs + m2, llf); m2 = 0; for (int p = 1; p <= n; p++) { for (auto e : G[p]) { ws[m2++] = e.w; } } for (int t = 0; t < S; t++) { int c = 0; for (int p = 1; p <= n; p++) { for (auto e : G[p]) { tmpw[c] = fs[t][p]; c++; } } c = 0; for (int p = 1; p <= n; p++) { for (auto e : G[p]) { vmin(vs[c], tmpw[c] + ft[S - t - 1][e.v] + e.w); c++; } } } for (int i = 0; i < m2; i++) { if (vs[i] ^ llf) { E.emplace_back(ws[i] << 1, vs[i]); } } sort(E.begin(), E.end(), greater<pair<int, ll>>()); int tp = 0; for (auto e : E) { while (tp && stk[tp].first == e.first) vmin(e.second, stk[tp--].second); while (tp >= 2 && better(stk[tp - 1], stk[tp]) >= better(stk[tp], e)) tp--; stk[++tp] = e; } Zi ret = 0; // S + 2 * t <= X ll lim = (X - S) >> 1, lst = 1; for (int i = 1; i <= tp && lst <= lim; i++) { int k = stk[i].first; ll b = stk[i].second; ll R = min(lim + 1, i == tp ? lim + 1 : better(stk[i], stk[i + 1])); if (R <= lst) { continue; } // \sum_{j = lst}^{R - 1} (b + k * j) ret += Zi(b) * (R - lst) + sum2(lst, R - 1) * k; lst = R; } return ret; } void solve() { scanf("%d%d%d", &n, &m, &X); for (int i = 1; i <= n; i++) { G[i].clear(); } for (int i = 1, u, v, w; i <= m; i++) { scanf("%d%d%d", &u, &v, &w); G[u].emplace_back(v, w); if (u ^ v) { G[v].emplace_back(u, w); } } int T = min((n << 1) + 10, X); do_dp(1, T, fs); do_dp(n, T, ft); Zi ans = 0; for (int i = 1; i <= T; i++) { if (fs[i][n] < llf) { ans += fs[i][n]; } } if (X > T) { ans += solve(0, T); ans += solve(1, T); } printf("%d\n", ans.v); } int main() { int T; scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem J Skills
考虑 3 维 dp 滚动后实际转移是怎么样的:
- 每行每列求个最值
- 每行每个做一个等差数列加
显然这能斜率优化,维护 $O(n)$ 个凸包就可以了
写完感觉自己代码长度不太对,瞄了一下题解。怪诶,为什么我印象里值域是 $10^9$
Emmmm.......值域很小,所以一个技能不会连续不学太多天。
Code
#include <bits/stdc++.h> using namespace std; #define for_st(_x, _y) for (int _x = 0; (_x) < 3; (_x)++) for (int _y = 0; (_y) < 3; (_y)++) if ((_x) ^ (_y)) const signed int inf = 1e9 + 9; const int N = 1005; template <typename T> void pfil(T* pst, T* ped, T val) { for ( ; pst != ped; *(pst++) = val); } template <typename T> bool vmax(T& a, T b) { return (a < b) ? (a = b, true) : false; } typedef class dp_t { public: int tg, tp, fr; vector<int> f; vector<int> g; vector<int> stk; void init(int n) { tg = 0, fr = tp = 0; f.resize(n + 1); g.resize(n + 1); stk.resize(n + 1); for (int i = 0; i <= n; i++) { f[i] = g[i] = -inf; stk[i] = 0; } } void add(int v) { tg += v; } // x < y int better(int x, int y) { return (g[x] - g[y]) / (y - x); } void append(int t, int v) { f[t] = v - tg; g[t] = f[t] - ((t * (t + 1)) >> 1); while (tp > fr + 1 && better(stk[tp - 1], stk[tp]) >= better(stk[tp], t)) tp--; while (tp > fr && g[stk[tp]] <= g[t]) tp--; stk[++tp] = t; } int calc(int x, int t) { return f[x] + tg - (((t - x) * (t - x - 1)) >> 1); } int query(int t) { if (fr == tp) return -inf; int tmp = calc(stk[fr + 1], t), foo; while (fr + 1 < tp && (foo = calc(stk[fr + 2], t)) >= tmp) { tmp = foo; fr++; } return tmp; } void log() { cerr << fr << " " << tp << " " << tg << " " << endl; for (auto x : f) cerr << x << ' '; cerr << endl; for (auto x : g) cerr << x << ' '; cerr << endl; for (int i = 1; i <= tp; i++) cerr << stk[i] << ' '; cerr << endl; } } dp_t; int T, n; int a[N][3]; int f[N][3]; int tmpv[3][3][N]; dp_t g[3][3]; // first is the outside index dp_t h[3][3][N]; // dp_t h01[N], h02[N], h12[N]; // dp_t h10[N], h20[N], h21[N]; void solve() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 0; j < 3; j++) { scanf("%d", a[i] + j); f[i][j] = f[i - 1][j] + a[i][j]; } } int ans = max(f[n][0], max(f[n][1], f[n][2])); for_st(x, y) g[x][y].init(n); for_st(x, y) { for (int i = 1; i <= n; i++) { h[x][y][i].init(n); } } for (int i = 2; i <= n; i++) { for_st(x, y) { for (int j = 1; j < i - 1; j++) { tmpv[x][y][j] = g[x][y].calc(j, i); } } for_st(x, y) { int z = 3 - x - y; for (int j = 1; j < i - 1; j++) { vmax(tmpv[z][y][j], h[x][y][j].query(i)); } } for_st(x, y) { int z = 3 - x - y; for (int j = 1; j < i - 1; j++) { h[x][y][j].append(i - 1, tmpv[x][y][j]); h[x][y][j].add(a[i][z] - (i - j)); h[y][x][i - 1].append(j, tmpv[x][y][j] + (((i - j) * (i - j - 1)) >> 1)); } h[y][x][i - 1].add(a[i][z] - 1); } for_st(x, y) tmpv[x][y][0] = max(f[i - 1][y], g[y][x].query(i)); for_st(x, y) { g[x][y].append(i - 1, tmpv[x][y][0]); g[x][y].add(a[i][x]); } } // h[0][1][3].log(); for_st(x, y) { vmax(ans, g[x][y].query(n + 1)); for (int i = 1, tmp; i < n; i++) { vmax(ans, tmp = h[x][y][i].query(n + 1)); // cerr << x << " " << y << " " << i << " " << tmp << endl; } } printf("%d\n", ans); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem L Tree Distance
考虑 $dist(u, v) = dep(u) + dep(v) - 2 \times dep(lca(u, v))$
考虑点分治,考虑硬点分治中心是路径的 lca,因为 lca 深度那一项是减,所以如果不是真正的 lca 的话存在更小的答案。
那么对于一个分治区域内的点,肯定是选择深度最小的和次小的作为答案,考虑一对点 $(u, v)$ 不妨设 $u < v$,如果能贡献到答案,那么要满足 $[u, v]$ 中 $u, v$ 的深度是最小值或者次小值,讨论 $u$ 是区间最小还是 $v$ 是区间最小,然后搞个单调栈就可以了。
现在只有 $O(n\log n)$ 个候选点对。直接扫描线处理询问就可以了。
upd:这里扫描线因为只有添加点,查后缀,所以可以用树状数组(
Code
#include <bits/stdc++.h> using namespace std; #define ll long long class Input { public: #define digit(_x) ((_x) >= '0' && (_x) <= '9') template <typename T> Input& operator >> (T &u) { char x; while (~(x = getchar()) && !digit(x)); for (u = x - '0'; ~(x = getchar()) && digit(x); u = u * 10 + x - '0'); return *this; } } in; class Output { public: Output& operator << (ll u) { static char stk[30]; char *p = stk + 26; *p = 0; if (u < 0) { putchar('-'); u = -u; } if (!u) *(--p) = '0'; while (u) { *(--p) = u % 10 + '0'; u /= 10; } puts(p); return *this; } } out; template <typename T> bool vmin(T& a, T b) { return a > b ? (a = b, true) : false; } const ll llf = 2e18; const int N = 2e5 + 5, M = 1e6 + 5; typedef class Event { public: int op, l, r; ll v; Event(int op, int l, int r, ll v) : op(op), l(l), r(r), v(v) { } bool operator < (Event b) const { return r == b.r ? op < b.op : r < b.r; } } Event; typedef class Edge { public: int ed, w; Edge(int ed, int w) : ed(ed), w(w) { } } Edge; typedef class Node { public: ll v; Node *l, *r; void upd(ll x) { vmin(v, x); } void push_up() { v = min(l->v, r->v); } } Node; Node pool[N << 1]; Node* tp = pool; typedef class SegmentTree { public: int n; Node* rt; SegmentTree(int n) : n(n) { build(rt, 1, n); } void build(Node*& p, int l, int r) { p = tp++; p->v = llf; if (l ^ r) { int mid = (l + r) >> 1; build(p->l, l, mid); build(p->r, mid + 1, r); } } void modify(Node* p, int l, int r, int x, ll v) { if (l == r) { p->upd(v); return; } int mid = (l + r) >> 1; if (x <= mid) { modify(p->l, l, mid, x, v); } else { modify(p->r, mid + 1, r, x, v); } p->push_up(); } void modify(int p, ll v) { modify(rt, 1, n, p, v); } ll query(Node* p, int l, int r, int ql, int qr) { if (l == ql && r == qr) { return p->v; } int mid = (l + r) >> 1; if (qr <= mid) { return query(p->l, l, mid, ql, qr); } else if (ql > mid) { return query(p->r, mid + 1, r, ql, qr); } ll vl = query(p->l, l, mid, ql, mid); ll vr = query(p->r, mid + 1, r, mid + 1, qr); return min(vl, vr); } ll query(int l, int r) { return query(rt, 1, n, l, r); } } SegmentTree; int n, q; ll ans[M]; vector<Edge> G[N]; vector<Event> E; int sz[N]; bool ban[N]; int get_sz(int p, int fa) { sz[p] = 1; for (auto E : G[p]) { int e = E.ed; sz[p] += ((e == fa || ban[e]) ? 0 : get_sz(e, p)); } return sz[p]; } int get_G(int p, int fa, int half_size) { for (auto E : G[p]) { int e = E.ed; if ((e ^ fa) && !ban[e] && sz[e] > half_size) { return get_G(e, p, half_size); } } return p; } ll dep[N]; vector<int> V; void get_dep(int p, int fa, ll d) { V.push_back(p); dep[p] = d; for (auto E : G[p]) { int e = E.ed; if ((e ^ fa) && !ban[e]) { get_dep(e, p, d + E.w); } } } int stk[N]; void dividing(int x) { int g = get_G(x, 0, get_sz(x, 0) >> 1); ban[g] = true; V.clear(); get_dep(g, 0, 0); int tp = 0, q; sort(V.begin(), V.end()); for (auto p : V) { while (tp && dep[q = stk[tp]] >= dep[p]) { E.emplace_back(0, q, p, dep[q] + dep[p]); tp--; } stk[++tp] = p; } reverse(V.begin(), V.end()); tp = 0; for (auto p : V) { while (tp && dep[q = stk[tp]] > dep[p]) { E.emplace_back(0, p, q, dep[p] + dep[q]); tp--; } stk[++tp] = p; } for (auto E : G[g]) { int e = E.ed; if (!ban[e]) { dividing(e); } } } int main() { in >> n; for (int i = 1, u, v, w; i < n; i++) { in >> u >> v >> w; G[u].emplace_back(v, w); G[v].emplace_back(u, w); } in >> q; for (int i = 1, l, r; i <= q; i++) { in >> l >> r; if (l == r) { ans[i] = -1; } else { E.emplace_back(1, l, r, i); } } dividing(1); sort(E.begin(), E.end()); SegmentTree st (n); for (auto e : E) { if (!e.op) { st.modify(e.l, e.v); // cerr << "M " << e.l << " " << e.v << endl; } else { ans[e.v] = st.query(e.l, e.r); // cerr << "Q " << e.l << " " << e.r << endl; } } for (int i = 1; i <= q; i++) { out << ans[i]; } return 0; }