题解:洛谷 AT_abc466_c Count Close Pairs

【题目来源】

洛谷:AT_abc466_c [ABC466C] Count Close Pairs

【题目描述】

本题是一道 交互式问题(你编写的程序与评测程序通过标准输入输出进行交互的问题)。

在数轴上,点 \(1\) , \(2\) , \(\ldots\) , \(N\) 按此顺序 从左到右排列。

最初,你只会得到整数 \(N\)

之后,你可以向评测程序进行最多 \({2N}\) 次以下提问:

  • 选择满足 \(1 \leq i < j \leq N\) 的整数 \(i,j\) ,询问点 \(i\) 和点 \(j\) 之间的距离是否不超过 \(1\)

请输出距离不超过 \(1\) 的点对数量,即满足 \(1\leq i<j\leq N\) 且点 \(i\) 和点 \(j\) 之间的距离不超过 \(1\) 的整数对 \((i,j)\) 的数量。

【输入输出】

首先,从标准输入接收表示点数的整数 \(N\)

\(N\)

接着,你可以向评测程序重复提问,最多 \(2N\) 次。

提问请按以下格式输出到标准输出。其中, \(i,j\) 必须是满足 \(1 \leq i<j \leq N\) 的整数。

? \(i\) \(j\)

作为对此提问的响应,标准输入会给出以下两种回复之一:

Yes
No

其中,Yes 表示点 \(i\) 和点 \(j\) 之间的距离不超过 \(1\)No 表示点 \(i\) 和点 \(j\) 之间的距离大于 \(1\)

当得出问题的答案 \(X\) 后,请按以下格式输出答案。随后,请立即结束程序。

! \(X\)

【核心思想】

  1. 问题分析:数轴上有 \(N\) 个按顺序排列的点,具体坐标未知。通过最多 \(2N\) 次交互询问判断点对 \((i,j)\) 距离是否 \(\leq 1\),求满足条件的点对总数。这是一个交互式双指针问题,核心在于利用距离单调性,通过双指针扫描高效统计满足条件的点对,同时严格控制询问次数在 \(2N\) 以内。

  2. 算法选择

    • 双指针(Two Pointers):维护区间 \([l, r]\),利用单调性——若 \((l, r)\) 距离 \(> 1\),则对于固定 \(l\),所有 \(r' > r\) 的距离也 \(> 1\);若 \((l, r)\) 距离 \(\leq 1\),则 \(r\) 可以右移扩大区间
    • 交互式贪心:每次询问后根据回答调整指针,确保每个点作为左端点或右端点最多被询问 \(2\)
  3. 关键步骤

    • 初始化\(l \leftarrow 1\)\(r \leftarrow 2\)\(ans \leftarrow 0\)
    • 双指针扫描\(r \leq N\)):
      • 询问 \((l, r)\) 的距离是否 \(\leq 1\)
      • 若回答 No(距离 \(> 1\)):
        • 区间 \([l, r-1]\) 内所有点与 \(l\) 的距离均 \(\leq 1\),累加 \(ans \leftarrow ans + (r - l - 1)\)
        • \(l \leftarrow l + 1\)(左端点右移)
        • \(l = r\),则 \(r \leftarrow r + 1\)
      • 若回答 Yes(距离 \(\leq 1\)):
        • \(r \leftarrow r + 1\)(右端点右移)
    • 处理剩余区间:当 \(r > N\) 时,区间 \([l, N]\) 内所有点对距离均 \(\leq 1\),累加 \(ans \leftarrow ans + \sum_{i=l}^{N-1} (N - i)\)
    • 输出答案! ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\),双指针过程中 \(l\)\(r\) 均最多移动 \(N\)
    • 空间复杂度:\(O(1)\),仅使用常数额外空间
    • 询问次数:最多 \(2N\) 次,满足题目限制
  5. 双指针与交互式策略的核心思想

    • 单调性利用:点按顺序排列,若 \((l, r)\) 距离 \(> 1\),则对于固定 \(l\),所有 \(r' > r\) 的距离也必然 \(> 1\)。因此一旦得到 No,即可确定以 \(l\) 为左端点的所有有效点对都在 \([l, r-1]\)
    • 指针移动的不变性:维护区间 \([l, r]\) 满足——\(l\)\([l, r-1]\) 内所有点距离均 \(\leq 1\),而 \(l\)\(r\) 的距离 \(> 1\)(或 \(r > N\))。每次 No 时可一次性统计 \(l\) 的所有有效点对
    • 询问次数控制:每个点最多作为左端点被询问 \(1\) 次(\(l\) 右移时),最多作为右端点被询问 \(1\) 次(\(r\) 右移时),总计不超过 \(2N\)
    • 剩余区间公式化:当 \(r\) 超出范围后,\([l, N]\) 内所有点对距离均 \(\leq 1\),直接通过公式计算,无需额外询问
    • 适用于有序序列上的区间统计、交互式信息获取类问题

【算法标签】

普及 #双指针

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int n;                             // n: 数轴上的点数
int l = 1, r = 2;                  // l: 当前区间的左端点; r: 当前区间的右端点
int ans;                           // ans: 距离不超过 1 的点对数量
string s;                          // s: 评测程序的回复("Yes" 或 "No")

int main()
{
    cin >> n;                      // 读入点数 N

    while (r <= n)                  // 双指针扫描所有可能的点对
    {
        cout << "? " << l << " " << r << endl;  // 询问点 l 和点 r 的距离是否不超过 1
        cin >> s;                  // 读入评测程序的回复

        if (s == "No")             // 如果距离超过 1
        {
            ans += r - l - 1;      // 区间 [l, r) 内的所有点与 l 的距离都不超过 1,累加这些点对
            l++;                   // 左端点右移,缩小当前区间

            if (l == r)            // 如果左右端点重合
                ++r;               // 右端点右移,扩大区间
        }
        else                        // 如果距离不超过 1
            ++r;                   // 右端点右移,扩大当前区间
    }

    for (int i = l; i < n; i++)   // 处理剩余区间内的点对
        ans += n - i;              // 累加从 i 出发到 n 的所有点对

    cout << "! " << ans << endl;   // 输出最终答案

    return 0;
}
posted @ 2026-07-13 10:56  团爸讲算法  阅读(18)  评论(0)    收藏  举报