1 条题解
-
0
思路分析
操作的本质是:对于每个 ,我们可以独立决定是否交换位置 与 上的骑士。用一个二进制变量 表示是否交换( 表示不交换, 表示交换)。那么最终位置 (-indexed)上的骑士为:
- 若 :当 时为 ,否则为 ;
- 若 :当 时为 ,否则为 。
每张课桌 包含两个连续的位置 和 ,它们分别属于不同的“对”(因为 时 与 相差 ,而同一对的两个位置相差 )。定义位置 所属的对编号为 ,记为 。那么课桌 的两个对编号为: [ u_i = f(2i-1) = ((2i-2)\bmod n)+1,\qquad v_i = f(2i) = ((2i-1)\bmod n)+1. ] 它们满足 (模 意义下连续)。课桌 的总和为: [ S_i = \big(x_{u_i}? a_{u_i+n}:a_{u_i}\big) + \big(x_{v_i}? a_{v_i+n}:a_{v_i}\big). ] 因此每个 仅依赖于两个相邻的变量 。
将 个变量看作节点,每个课桌看作连接 与 的一条无向边,则每个节点恰好连接两条边(因为每个 会作为 出现在一个课桌,作为 出现在另一个课桌)。于是整个图是由若干个环构成的(可能包含两个节点之间有两条平行边的情况)。每个环内部变量相互依赖,不同环之间变量独立。
我们的目标是为每个节点 赋值,使得所有边的权值 的最大值与最小值之差最小。
处理单个环
对于一个有 个节点和 条边的环(节点按顺序 ,边 连接 与 ,下标模 ),每条边有四个可能的权值: [ w_i[a][b] = \big(a? a_{v_i+n}:a_{v_i}\big) + \big(b? a_{v_{i+1}+n}:a_{v_{i+1}}\big),\quad a,b\in{0,1}. ] 我们需要选择 使得所有 的最大值与最小值之差最小。
固定最小值求解最大值的最小值
固定一个下界 (即要求所有边权 ),定义函数 = 在满足边权 的前提下,环上边权最大值的最小可能值(若不可行则 )。则答案即为 ,其中 取遍所有可能出现的边权值(因为最优解的最小值一定是某个边权)。
对于固定的 ,可以在环上做 DP:
- 枚举 或 。
- 设 表示处理到节点 (已确定 ),且从边 到 的权值最大值的最小可能值。初始化 。
- 转移:对于 从 到 ,考虑边 ,已知 ,枚举 ,若 ,则新最大值 ,用其更新 。
- 处理完所有 条边后,需要 ,因此取 作为该起始状态的结果。
- 对两种起始状态取最小值,即为 。
单环的 DP 时间复杂度 。
合并多个环
因为不同环之间变量独立,所以全局的可行性要求每个环均存在赋值使得边权 。此时全局最大值为各环 的最大值。于是对于每个候选 ,计算: [ \text{全局最大值} = \max_{\text{环 }R} f_R(L),\quad \text{差值} = \text{全局最大值} - L, ] 取所有可行 中的最小差值即为答案。
候选 只需取所有边权的可能取值(共 个),排序去重后依次检查。总复杂度 ,因为 的总和不超过 。
特殊处理
当 时,只有一张课桌,总和恒为 ,差值为 ,直接输出。
算法步骤
- 读入 。
- 对每个测试用例:
- 若 ,输出 并跳过。
- 读入数组 。
- 构建图:对每个课桌 :
。
记录边 连接节点 与 ,并存储四个权值:
[
\begin{aligned}
w[i][0][0] &= a[u] + a[v] \
w[i][0][1] &= a[u] + a[v+n] \
w[i][1][0] &= a[u+n] + a[v] \
w[i][1][1] &= a[u+n] + a[v+n]
\end{aligned}
]
同时将边加入邻接表
adj[u].push_back({v,i}),adj[v].push_back({u,i})。 - 找出所有环(连通分量):
vis数组标记节点,对每个未访问节点进行 DFS/迭代,沿着度数为 的边走,收集节点序列和边序列,得到每个环的有向顺序。 - 收集所有可能的权值到列表
all_vals中。 - 初始化答案
ans = INF。 - 对
all_vals排序去重,依次枚举每个值作为 :global_max = 0,可行标志ok = true。 对每个环,调用calc_ring(ring, L)得到该环的最小可能最大值 ,若 则ok=false并跳出;否则global_max = max(global_max, f)。 若ok为真,则更新ans = min(ans, global_max - L)。 - 输出
ans。
复杂度分析
- 建图 。
- 找环 。
- 候选 数量 ,每个 对所有环做 DP,总环大小和为 ,故总时间 。
- 总和 ,实际运行很快。
参考代码(C++)
```cpp #include <bits/stdc++.h> using namespace std; const int INF = 2e9; int solve_ring(const vector<array<array<int,2>,2>>& w, int L) { int m = w.size(); if (m == 0) return -INF; auto dp = [&](int start) -> int { int cur[2] = {INF, INF}; cur[start] = -INF; for (int i = 0; i < m; ++i) { int nxt[2] = {INF, INF}; for (int a = 0; a < 2; ++a) { if (cur[a] >= INF) continue; for (int b = 0; b < 2; ++b) { int val = w[i][a][b]; if (val < L) continue; int cand = max(cur[a], val); if (cand < nxt[b]) nxt[b] = cand; } } cur[0] = nxt[0], cur[1] = nxt[1]; } return cur[start]; }; return min(dp(0), dp(1)); } int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n; cin >> n; vector<int> a(2 * n); for (int i = 0; i < 2 * n; ++i) cin >> a[i]; if (n == 1) { cout << "0\n"; continue; } vector<int> edge_u(n), edge_v(n); vector<array<array<int,2>,2>> edge_w(n); vector<vector<pair<int,int>>> adj(n); for (int i = 0; i < n; ++i) { int u = (2 * i) % n; int v = (2 * i + 1) % n; edge_u[i] = u, edge_v[i] = v; edge_w[i][0][0] = a[u] + a[v]; edge_w[i][0][1] = a[u] + a[v + n]; edge_w[i][1][0] = a[u + n] + a[v]; edge_w[i][1][1] = a[u + n] + a[v + n]; adj[u].emplace_back(v, i); adj[v].emplace_back(u, i); } vector<bool> vis(n, false); vector<vector<array<array<int,2>,2>>> rings; for (int s = 0; s < n; ++s) { if (vis[s]) continue; vector<int> nodes, edges; int cur = s, prev = -1; while (true) { vis[cur] = true; nodes.push_back(cur); int nxt_node = -1, nxt_edge = -1; for (auto &p : adj[cur]) { if (p.second == prev) continue; nxt_node = p.first; nxt_edge = p.second; break; } edges.push_back(nxt_edge); if (nxt_node == s) break; cur = nxt_node; prev = nxt_edge; } int m = nodes.size(); vector<array<array<int,2>,2>> ring_w(m); for (int j = 0; j < m; ++j) { int u = nodes[j], v = nodes[(j + 1) % m], ei = edges[j]; if (edge_u[ei] == u && edge_v[ei] == v) { ring_w[j] = edge_w[ei]; } else { for (int a = 0; a < 2; ++a) for (int b = 0; b < 2; ++b) ring_w[j][a][b] = edge_w[ei][b][a]; } } rings.push_back(move(ring_w)); } vector<int> vals; for (int i = 0; i < n; ++i) for (int a = 0; a < 2; ++a) for (int b = 0; b < 2; ++b) vals.push_back(edge_w[i][a][b]); sort(vals.begin(), vals.end()); vals.erase(unique(vals.begin(), vals.end()), vals.end()); int ans = INF; for (int L : vals) { int global_max = 0; bool ok = true; for (auto &ring : rings) { int cur = solve_ring(ring, L); if (cur >= INF) { ok = false; break; } if (cur > global_max) global_max = cur; } if (ok) ans = min(ans, global_max - L); } cout << ans << '\n'; } return 0; }
- 1
信息
- ID
- 7246
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者