2022 International Collegiate Programming Contest, Jinan Site 部分题目简要题解
从这里开始
Problem B Torch
注意到 的和不会超过
考虑胖先生的周期开始的时候,瘦先生的周期在时刻 ,距离胖先生的距离为 ,那么胖先生的周期结束的时候,瘦先生的距离胖先生的距离大概是 之类的东西,其中 是一个和 有关的常数。
这个信息不难合并。对于每个询问,相当于是若干个循环再加上一个前缀和之类的东西,然后再处理一下一个周期中零散的情况。
时间复杂度 。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #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 序在 的方案数相当于它到根节点的祖先,每个把自己节点做背包。同时每个祖先处做背包还要维护一下在这个点之前选了多少棵子树。
直接通过合并前后的背包复杂度会炸,改成二元多项式除就可以了。
Code
考场上写过了,感觉还有点小胖,懒得再写一遍了。
Problem F Grid Points
你看我这老年人像是会补 0 队 ac 的题目的吗

Problem G Quick Sort
模拟一下这个过程可以发现 的值要么是 要么是
考虑枚举两边中少的一边。找交换的时候另外一边的数的时候,考虑维护一下每个值的位置,然后把少的半边的值拉出来排序。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #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
首先特判 。
考虑判定一区间 是否可行。可以注意到如果有一个区间包含 ,那么在之后的合并过程中,我可以让它包含
如果一个区间包含 ,一个包含 ,那么显然我通过一次合并可以得到
现在考虑怎样凑出一个包含 的区间:
- 本身存在一个区间包含
- 两侧分别至少存在 1 个区间
显然除了这两种情况,剩下都是不可能的
考虑扫描线,计算 的时候可能的右端点的范围。
暴力的做法是枚举 是怎么凑出来的,删掉这些区间,然后求剩下的区间中最小的左端点 以及最大的右端点 ,可能的右端点的范围为
每次最多只会删掉 2 个区间,所以如果 足够大,这些可能的右端点的范围的并集就是所有的 取 min,右端点取 max(当 2 种凑 方案使得剩下的区间只要有 1 个公共的时候,可以保证这两个 不是相离的)。当 足够大的时候,要么就是删掉 个或者 个区间求 ,要么就是对所有区间的求。
如果 比较小,直接暴力就行了。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | #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
傻逼学院负责人
假设有一条路径绕一个很大的环(环大小至少为 ),设上面的最小的边为 ,那么我可以通过较短的一条路径走到 ,然后在 上左右横跳若干次,再折返回来,如果奇偶性不对那么这个环是个奇环,再把这个环绕一圈就可以了。
剩下就很傻逼了。给路径长度设一个阈值 ,然后求一下经过每条边,路径长度为 的最短路径。路径长度大于 的时候搞个凸包就可以了。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | #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 滚动后实际转移是怎么样的:
- 每行每列求个最值
- 每行每个做一个等差数列加
显然这能斜率优化,维护 个凸包就可以了
写完感觉自己代码长度不太对,瞄了一下题解。怪诶,为什么我印象里值域是
Emmmm.......值域很小,所以一个技能不会连续不学太多天。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | #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
考虑
考虑点分治,考虑硬点分治中心是路径的 lca,因为 lca 深度那一项是减,所以如果不是真正的 lca 的话存在更小的答案。
那么对于一个分治区域内的点,肯定是选择深度最小的和次小的作为答案,考虑一对点 不妨设 ,如果能贡献到答案,那么要满足 中 的深度是最小值或者次小值,讨论 是区间最小还是 是区间最小,然后搞个单调栈就可以了。
现在只有 个候选点对。直接扫描线处理询问就可以了。
upd:这里扫描线因为只有添加点,查后缀,所以可以用树状数组(
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | #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; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!