题解:洛谷 AT_abc466_c Count Close Pairs
【题目来源】
洛谷:AT_abc466_c [ABC466C] Count Close Pairs
【题目描述】
This is an interactive problem (in which your program and the judge program communicate via Standard Input and Output).
On a number line, points \(1\), \(2\), \(\ldots\), \(N\) are arranged from left to right in that order.
Initially, you are given only the integer \(N\).
Then, you can ask the judge the following question at most \({2N}\) times.
- Choose integers \(i\) and \(j\) satisfying \(1 \leq i < j \leq N\), and ask whether the distance between points \(i\) and \(j\) is at most \(1\).
Output the number of pairs of points whose distance is at most \(1\), that is, the number of pairs of integers \((i,j)\) satisfying \(1 \leq i < j \leq N\) such that the distance between points \(i\) and \(j\) is at most \(1\).
这是一个交互式问题(你的程序与评测程序通过标准输入输出进行通信)。
在数轴上,点 \(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)\) 的数量。
【交互过程】
First, receive the integer \(N\) representing the number of points from Standard Input:
\(N\)
Then, you can repeat the question described in the problem statement to the judge at most \(2N\) times.
Output the question to Standard Output in the following format, where \(i\) and \(j\) must be integers satisfying \(1 \leq i<j \leq N\):
? \(i\) \(j\)
As a response to this, one of the following will be given from Standard Input:
Yes
No
Here, Yes indicates that the distance between points \(i\) and \(j\) is at most \(1\), and No indicates that the distance between points \(i\) and \(j\) is greater than \(1\).
Once you have found the answer \(X\) to the problem, output your answer in the following format, and then terminate the program immediately:
! \(X\)
【核心思想】
-
问题分析:在数轴上有 \(N\) 个点按顺序排列,但具体坐标未知。通过最多 \(2N\) 次交互询问,判断点对 \((i,j)\) 的距离是否 \(\leq 1\),求距离不超过 \(1\) 的点对总数。这是一个交互式双指针问题,核心在于利用单调性通过双指针扫描高效统计满足条件的点对,同时控制询问次数在 \(2N\) 以内。
-
算法选择:
- 双指针(Two Pointers):维护区间 \([l, r]\),利用距离单调性——若 \((l, r)\) 距离 \(> 1\),则对于固定的 \(l\),所有 \(r' > r\) 的距离也 \(> 1\);若 \((l, r)\) 距离 \(\leq 1\),则 \(r\) 可以右移扩大区间
- 交互式贪心:每次询问后根据回答调整指针,确保每个点作为左端点或右端点最多被询问 \(2\) 次
-
关键步骤:
- 初始化:\(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
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),双指针过程中 \(l\) 和 \(r\) 均最多移动 \(N\) 次,每次一次询问
- 空间复杂度:\(O(1)\),仅使用常数额外空间
- 询问次数:最多 \(2N\) 次,满足题目限制
-
双指针与交互式策略的核心思想:
- 单调性利用:由于点按顺序排列,若 \((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\),直接通过公式计算,无需额外询问
- 适用于有序序列上的区间统计、交互式信息获取类问题,核心是利用单调性减少询问次数
- 单调性利用:由于点按顺序排列,若 \((l, r)\) 距离 \(> 1\),则对于固定的 \(l\),所有 \(r' > r\) 的距离也必然 \(> 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;
}
浙公网安备 33010602011771号