构造矩形
题目描述
现有 \(n\) 条长度为 \(m\) 的线段,垂直于 x 轴分布,且互不重合。第 \(i\) 条线段的两个端点均为整数点,分别为 \((a_i, 0)\) 和 \((a_i, m)\)。每条线段上有 \(m+1\) 个整数点,纵坐标分别为 \(0, 1, 2, …, m\)。
现在,你需要选择两条不同的线段,并在每一条线段上各选择两个不同的整数点,要求这四个点连接构成的四边形恰好是一个矩形,且矩形的长减去宽为 \(k\)。请问一共有多少种选择方案?
输入描述:
第一行输入三个整数 \(n, m, k (1 ≤ n, m ≤ 10^5; 1 ≤ k < m)\) 代表线段条数、线段的长度、要求构成的矩形长宽之差。
第二行输入 \(n\) 个整数 \(a_1, a_2, …, a_n (1 ≤ a_1 < a_2 < ⋯ < a_n ≤ 10^5)\) 代表第 \(i\) 条线段端点的横坐标。
输出描述:
输出一个整数,代表一共有多少种选择方案。我们可以证明,答案不会超过 \(1e18\)。
示例 1
输入:
3 2 1
1 2 3
输出:
4
说明:
在这个样例中,四种不同的选择方案如下图所示:
示例 2
输入:
6 9 4
1 2 3 4 5 8
输出:
70
题解
考虑通过枚举左侧线段 \(a_i\) 和右侧线段 \(a_j\) 构成矩形,记长为 \(l\) 宽为 \(w\) 分以下两种情况:
-
当 \(w = a_j - a_i\) , \(l = w + k = a_j - a_i + k\)
固定 \(a_i\) 向右枚举 \(a_j\) ,通过上下移动矩形,每一个合法的 \(a_j\) 可以有 \(m - l + 1\) 的贡献。因此第一部分总贡献如下。
\[\sum_{i=1}^{n}\sum_{j=i+1}^{n}[(m-l+1)\cdot(m-l+1>0)] \]可得
由于向右遍历 \(a_j\) ,\(l\) 单调变大。可用二分通过 \(m+a_i-k+1>a_j\) 确定最大的下标 \(j\) ,记 \(a_i\) 对应的 最大下标 \(j\) 为 \(R_i\)
可得
可通过前缀和进一步简化处理 \(\sum_{j=i+1}^{R_i}a_j\) ,记 \(a\) 的前缀和数组为 \(pref\)
则最终简化得
- 当 \(l = a_j - a_i\) , \(w = l - k = a_j - a_i - k\)
与上一种情况的区别仅为 \(k\) 的符号,此外注意到,此时可能出现 \(w\leqslant 0\) 的不合法情况。可得第二部分总贡献为
记 \(a\) 的前缀和数组为 \(pref\) , \(a_i\) 对应的 最小、最大下标 \(j\) 分别为 \(L_i\) 和 \(R_i\) ,分别由 \(w = l - k = a_j - a_i - k >0\) 和 \(m-w+1 = m-a_j+a_i+k+1 >0\) 求得。然后还需要满足 \(R_i > L_i\) 最后简化得
答案即两部分之和,
代码
#include <iostream>
#include <algorithm>
using namespace std;
using ll = long long;const ll N = 1e5 + 5;
ll n, m, k, a[N], pref[N];int main() {cin >> n >> m >> k;for (int i = 1; i <= n; i++) {cin >> a[i];pref[i] = pref[i - 1] + a[i];}ll ans = 0;// a[j] - a[i] 为长方形的宽for (ll i = 1;i <= n;i++){ll R = lower_bound(a + 1,a + n + 1,m+a[i]-k+1) - a;R--;ans += (R - i) * (m + a[i] - k + 1) - (pref[R] - pref[i]);}// a[j] - a[i] 为长方形的长for (ll i = 1;i <= n;i++){ll R = lower_bound(a + 1,a + n + 1,m+a[i]+k+1) - a;ll L = upper_bound(a + 1,a + n + 1,a[i]+k) - a;R--;if (L > R) continue;ans += (R - L + 1) * (m + a[i] + k + 1) - (pref[R] - pref[L -1]);}cout << ans << endl;
}
后记
有点累的,写的最长的一篇了
快写完了没保存以为要重写,没开自动保存但发现Typora还收着我的草稿救了我一命 ´ᯅ`