1 条题解

  • 0
    @ 2026-5-18 20:33:14

    一、题目重述

    nn 个城市排成一条直线,编号 11nn
    每个城市拥有 44 种颜色(B, G, R, Y)中的两种不同颜色,共有 66 种可能的颜色组合:

    • BG, BR, BY, GR, GY, RY

    移动规则:可以从城市 ii 移动到城市 jj,当且仅当它们有至少一种相同颜色,花费为 ij|i-j|

    回答 qq 个独立的询问:给定 x,yx, y,求从 xxyy 的最小花费,若不可达则输出 1-1

    数据范围

    • t104t \le 10^4 个测试用例
    • 所有测试用例的 nn 之和 2×105\le 2 \times 10^5
    • 所有测试用例的 qq 之和 2×105\le 2 \times 10^5
    • 1x,yn1 \le x, y \le n

    二、关键观察

    2.1 直接移动的条件

    SxS_x 为城市 xx 拥有的颜色集合(Sx=2|S_x|=2)。
    SxSyS_x \cap S_y \ne \varnothing,则 xxyy 可以直接移动,花费为:

    costdirect=xy\text{cost}_{\text{direct}} = |x - y|

    这是理论下界,因为任何路径的花费 \ge 直线距离(三角不等式)。

    2.2 间接移动的路径结构

    SxSy=S_x \cap S_y = \varnothing,则需要经过一个中间城市 zz,使得:

    $$S_z \cap S_x \ne \varnothing \quad \text{且} \quad S_z \cap S_y \ne \varnothing $$

    总花费为:

    xz+zy|x - z| + |z - y|

    因为 SzS_z 只有 22 种颜色,且 SxS_xSyS_y 不相交(共 44 种不同颜色),所以 SzS_z 必须恰好包含 SxS_x 中的一种颜色和 SyS_y 中的一种颜色。
    因此,可能的 SzS_z2×2=42 \times 2 = 4 种。


    三、颜色编码与互补性质

    66 种颜色组合编码为 050 \sim 5

    组合 编码 包含颜色
    BG 0 {B, G}
    BR 1 {B, R}
    BY 2 {B, Y}
    GR 3 {G, R}
    GY 4 {G, Y}
    RY 5 {R, Y}

    重要性质:两种颜色组合 uuvv 有公共颜色 当且仅当 u+v5u + v \ne 5

    验证:

    • 0+5=50+5=5:BG 与 RY 无公共颜色 ✅
    • 1+4=51+4=5:BR 与 GY 无公共颜色 ✅
    • 2+3=52+3=5:BY 与 GR 无公共颜色 ✅
    • 其他组合相加 5\ne 5 的都有公共颜色(例如 0+1=10+1=1:BG 与 BR 共有 B)

    这个性质的由来:互补的两种组合正好覆盖全部 44 种颜色且无交集。

    因此:

    有公共颜色    a[x]+a[y]5\text{有公共颜色} \iff a[x] + a[y] \ne 5

    四、预处理:最近的城市位置

    我们需要快速回答:对于某个城市 ii 和某种颜色组合 jj0j50 \le j \le 5),离 ii 最近的、颜色组合为 jj 的城市在哪里?
    分别考虑左边和右边。

    4.1 左边最近 (lf)

    定义 lf[i][j] 为:在 ii 左边(含 ii 本身)最近的、颜色组合为 jj 的城市下标(00-based)。若不存在,设为 -\infty

    预处理方法(正向扫描):

    vector<int> last(6, -INF);
    for (int i = 0; i < n; ++i) {
        last[a[i]] = i;
        lf[i] = last;  // 复制数组
    }
    

    4.2 右边最近 (rg)

    定义 rg[i][j] 为:在 ii 右边(含 ii 本身)最近的、颜色组合为 jj 的城市下标。若不存在,设为 ++\infty

    预处理方法(反向扫描,或反转数组后正向扫描再反转):

    vector<int> last(6, INF);
    for (int i = n-1; i >= 0; --i) {
        last[a[i]] = i;
        rg[i] = last;
    }
    

    标程中用了反转数组的技巧,效果等价。


    五、询问处理

    给定 x,yx, y(已转为 00-based),我们想求最小花费。

    5.1 枚举中间城市颜色

    我们枚举所有可能的中间城市颜色 jj0j50 \le j \le 5),但必须满足:

    • jja[x]a[x] 有公共颜色     a[x]+j5\iff a[x] + j \ne 5
    • jja[y]a[y] 有公共颜色     a[y]+j5\iff a[y] + j \ne 5

    同时满足这两个条件的 jj 就是合法的中间城市颜色。

    5.2 使用预处理的最近位置

    对于合法的 jj,我们取 xx 左边最近的 jj 颜色城市 zl=lf[x][j]z_l = lf[x][j] 和右边最近的 jj 颜色城市 zr=rg[x][j]z_r = rg[x][j](如果存在)。

    计算花费:

    $$\text{cost}_l = |x - z_l| + |z_l - y|,\quad \text{cost}_r = |x - z_r| + |z_r - y| $$

    取所有合法 jj 对应的这些值的最小值。

    5.3 为什么只检查 xx 附近的 jj 城市?

    正确性证明(简要):

    考虑最优路径 xzyx \to z \to y,其中 zz 的颜色为 jj

    • 如果 zxz \le x,那么 lf[x][j]lf[x][j]x\le x 的最大的 jj 颜色城市,因此 lf[x][j]zlf[x][j] \ge z
      由三角不等式:$$|x - lf| + |lf - y| \le |x - z| + |z - lf| + |lf - y| = |x - z| + |z - y| $$因为 zlf+lfyzy|z - lf| + |lf - y| \ge |z - y|xlfxz|x - lf| \le |x - z|,实际上需要更细致的分析,但已知结论是:用最近的那个替换 zz 不会增加总距离。严格证明需要分情况讨论,但这是 Codeforces 官方解法,已被验证正确。
    • 如果 zxz \ge x,同理考虑 rg[x][j]rg[x][j]

    因此,只需检查 xx 左右最近的 jj 城市即可。

    5.4 直接移动的情况

    a[x]+a[y]5a[x] + a[y] \ne 5 时,xxyy 本身就可以直接移动。
    此时,取 j=a[y]j = a[y],那么 lf[x][j]lf[x][j] 可能等于 yy 或某个更近的点,但计算出的 xz+zy|x - z| + |z - y| 的最小值恰好是 xy|x - y|(当 z=yz = y 时)。
    所以枚举过程自动覆盖了直接移动的情况,无需额外判断。


    六、算法步骤总结

    1. 编码:将 66 种颜色组合映射到 050 \sim 5,利用互补和为 55 的性质。
    2. 预处理
      • 正向扫描,计算 lf[i][j]ii 左边(含)最近的 jj 颜色城市下标。
      • 反向扫描,计算 rg[i][j]ii 右边(含)最近的 jj 颜色城市下标。
    3. 处理询问
      • 枚举 j=05j = 0 \dots 5,若 a[x]+j5a[x] + j \ne 5a[y]+j5a[y] + j \ne 5
        • lf[x][j]lf[x][j] 存在(>> -\infty),更新答案 min(ans, |x - lf| + |lf - y|)
        • rg[x][j]rg[x][j] 存在(<< \infty),更新答案 min(ans, |x - rg| + |rg - y|)
      • 若答案仍为无穷大,输出 1-1;否则输出答案。

    七、复杂度分析

    • 预处理:O(n6)=O(n)O(n \cdot 6) = O(n)
    • 每个询问:枚举 66jj,常数次操作,O(1)O(1)
    • 总复杂度:O(n+q)O(n + q) 每个测试用例,所有测试用例总和 O(2×105)O(2\times10^5),非常高效。

    八、完整代码(带详细注释)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int INF = 1e9;
    // 6 种颜色组合的编码顺序
    const string vars[] = {"BG", "BR", "BY", "GR", "GY", "RY"};
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        int t;
        cin >> t;
        while (t--) {
            int n, q;
            cin >> n >> q;
            
            vector<int> a(n);  // 每个城市的颜色组合编码 0..5
            for (int i = 0; i < n; ++i) {
                string s;
                cin >> s;
                a[i] = find(vars, vars + 6, s) - vars;
            }
            
            // lf[i][j] : i 左边(含)最近的 j 颜色城市下标,不存在为 -INF
            // rg[i][j] : i 右边(含)最近的 j 颜色城市下标,不存在为 INF
            vector<vector<int>> lf(n), rg(n);
            
            // 正向扫描,计算左边最近
            vector<int> last(6, -INF);
            for (int i = 0; i < n; ++i) {
                last[a[i]] = i;
                lf[i] = last;  // 复制数组
            }
            
            // 反向扫描,计算右边最近
            // 标程用了反转数组的技巧,这里直接反向扫描更直观
            vector<int> last2(6, INF);
            for (int i = n - 1; i >= 0; --i) {
                last2[a[i]] = i;
                rg[i] = last2;
            }
            
            while (q--) {
                int x, y;
                cin >> x >> y;
                --x; --y;  // 转为 0-based
                
                int ans = INF;
                // 枚举中间城市的颜色组合 j
                for (int j = 0; j < 6; ++j) {
                    // j 必须与 x 有公共颜色 且 与 y 有公共颜色
                    if (a[x] + j != 5 && a[y] + j != 5) {
                        // 检查 x 左边最近的 j 颜色城市
                        int z = lf[x][j];
                        if (z > -INF) {
                            ans = min(ans, abs(x - z) + abs(z - y));
                        }
                        // 检查 x 右边最近的 j 颜色城市
                        z = rg[x][j];
                        if (z < INF) {
                            ans = min(ans, abs(x - z) + abs(z - y));
                        }
                    }
                }
                
                if (ans > INF / 2) ans = -1;
                cout << ans << '\n';
            }
        }
        
        return 0;
    }
    

    九、正确性总结

    1. 直接移动:当 a[x]+a[y]5a[x] + a[y] \ne 5 时,枚举到 j=a[y]j = a[y] 就会得到 z=yz = y,花费 xy|x-y|,是最优的。
    2. 间接移动:当 a[x]+a[y]=5a[x] + a[y] = 5 时,必须经过中间城市。由于颜色组合只有 66 种,我们枚举所有可能的 jj,并利用预处理的最近位置,保证能找到最优的 zz。因为最优的 zz 一定是离 xx 最近的某种 jj 颜色城市之一(否则可以用更近的替换而不增加总距离)。
    3. 不可达:若没有任何合法 jj 使得存在 zz,则输出 1-1
    • 1

    信息

    ID
    7227
    时间
    1000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    2
    已通过
    1
    上传者