2020-08-08 提高組模擬賽選講
阿新 • • 發佈:2020-08-18
Problem T2. 作業
考慮狀壓 DP
\(f[mask]\) 表示當前已經排好序的顏色狀態為 \(mask\),最少的交換次數。
預處理移動的最小代價即可。
時間複雜度 \(\mathcal O(400\times 2^{20}+20n\log n)\)
#include <bits/stdc++.h> using namespace std; const int N = 300005, M = 21; int n; vector<int> pos[M]; long long _move[M][M]; long long dp[1 << M]; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for (int i = 1; i <= n; ++i) { int x; cin >> x; pos[x].push_back(i); } for (int i = 1; i <= 20; ++i) { for (int j = 1; j <= 20; ++j) { if (i == j) continue; _move[i][j] = 0; for (int k = 0; k < (int)pos[i].size(); ++k) { if (pos[j].size() == 0 || pos[j][0] > pos[i][k]) continue; _move[i][j] += lower_bound(pos[j].begin(), pos[j].end(), pos[i][k]) - pos[j].begin(); } } } memset(dp, 0x3f, sizeof dp); dp[0] = 0; for (int mask = 0; mask < (1 << 20); ++mask) { for (int j = 0; j < 20; ++j) { if (!(mask >> j & 1)) { long long res = 0; for (int k = 0; k < 20; ++k) { if (mask >> k & 1) { res += _move[k + 1][j + 1]; } } dp[mask | (1 << j)] = min(dp[mask | (1 << j)], dp[mask] + res); } } } cout << dp[(1 << 20) - 1] << '\n'; return 0; }
Problem T3. Five-seventeen
將兩個路徑拆成向上和向下的路徑,考慮樹上倍增維護答案
為了減少邊界的處理,令 \(prep_{i,j}\) 表示從 \(i\) 的父親開始,向上倍增 \(j\) 次的答案
時間複雜度 \(\mathcal O(10\times n\log n)\)
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 1; struct edge { int to, nxt; } e[N << 1]; int head[N]; int edge_cnt; void add_edge(int u, int v) { e[++edge_cnt] = (edge){v, head[u]}; head[u] = edge_cnt; } const int LOG = 17; int n, m, q; int bz[LOG + 1][N][11]; vector<int> tag[N]; int father[LOG + 1][N]; int dep[N]; void Dfs(int u, int Father) { father[0][u] = Father; dep[u] = dep[Father] + 1; for (int i = 1; i <= LOG; ++i) father[i][u] = father[i - 1][father[i - 1][u]]; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v == Father) continue; Dfs(v, u); } } int lca(int u, int v) { if (dep[u] < dep[v]) swap(u, v); for (int i = LOG; i >= 0; --i) if (dep[father[i][u]] >= dep[v]) u = father[i][u]; if (u == v) return u; for (int i = LOG; i >= 0; --i) if (father[i][u] != father[i][v]) u = father[i][u], v = father[i][v]; return father[0][u]; } int c[11]; void merge(int* a, int* b) { int x = 1, y = 1; c[0] = 0; for (int i = 1; i <= 10; ++i) { if (x == a[0] + 1 && y == b[0] + 1) break; else if (x == a[0] + 1) c[++c[0]] = b[y++]; else if (y == b[0] + 1) c[++c[0]] = a[x++]; else c[++c[0]] = a[x] < b[y] ? a[x++] : b[y++]; } for (int i = 0; i <= c[0]; ++i) a[i] = c[i]; } void merge(int* a, vector<int> b) { int x = 1, y = 0; c[0] = 0; for (int i = 1; i <= 10; ++i) { if (x == a[0] + 1 && y >= (int)b.size()) break; else if (x == a[0] + 1) c[++c[0]] = b[y++]; else if (y >= (int)b.size()) c[++c[0]] = a[x++]; else c[++c[0]] = a[x] < b[y] ? a[x++] : b[y++]; } for (int i = 0; i <= c[0]; ++i) a[i] = c[i]; } void Dfs_1(int u) { for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v == father[0][u]) continue; Dfs_1(v); merge(bz[0][v], tag[u]); } } void climb_up(int u, int v, int* ans) { for (int i = LOG; i >= 0; --i) { if (dep[father[i][u]] > dep[v]) { merge(ans, bz[i][u]); u = father[i][u]; } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> m >> q; for (int i = 1; i < n; ++i) { int u, v; cin >> u >> v; add_edge(u, v); add_edge(v, u); } Dfs(1, 0); for (int i = 1; i <= n; ++i) tag[i].clear(); for (int i = 1; i <= m; ++i) { int x; cin >> x; tag[x].push_back(i); } Dfs_1(1); for (int i = 1; i <= n; ++i) { sort(bz[0][i] + 1, bz[0][i] + bz[0][i][0] + 1); sort(tag[i].begin(), tag[i].end()); tag[i].resize(min((int)tag[i].size(), 10)); } for (int i = 1; i <= LOG; ++i) for (int j = 1; j <= n; ++j) { for (int k = 0; k <= bz[i][j][0]; ++k) bz[i][j][k] = bz[i - 1][j][k]; merge(bz[i][j], bz[i - 1][father[i - 1][j]]); } int ans[11]; for (int qt = 1; qt <= q; ++qt) { int u, v, k; cin >> u >> v >> k; int _lca = lca(u, v); ans[0] = 0; merge(ans, tag[u]); if (u != v) merge(ans, tag[v]); if (u == _lca) { climb_up(v, u, ans); } else if (v == _lca) { climb_up(u, v, ans); } else { merge(ans, tag[_lca]); climb_up(u, _lca, ans); climb_up(v, _lca, ans); } cout << min(k, ans[0]) << ' '; for (int i = 1; i <= min(k, ans[0]); ++i) cout << ans[i] << ' '; cout << '\n'; } return 0; }