题目链接

​http://codeforces.com/problemset/problem/1156/E​

题目大意

给定一个长度为 \(n\) 的排列 \(p\)(\(p\) 由 \(1, 2, \ldots, n\) 组成且在排列 \(p\) 中数字 \(1 \sim n\)

我们定义排列 \(p\) 的一个子段 \(p[l, r]\) 为一个 特殊子段 当且仅当 \(p_l + p_r = \max \limits_{i = l}^{r} p_i\)。

请计算排列 \(p\)

解题思路

分治。

考虑区间 \([l, r]\) 分成两端 \([l, mid]\) 和 \([mid+1, r]\)。

且左端点在 \([l, mid]\) 中,右端点在 \([mid+1, r]\) 中,然后使用双指针对于特定区间 \([i, j]\)

先假设最大值在 \([mid+1, j]\) 中(设为 \(mx\)),则可以得到最小的一个 \(i\),同时将 \([i, mid]\) 区间对应的数值加入一个 set 中,对于 \(p_j\),若 \(mx - p_j\) 在 set 中,则说明 \([i, mid]\)

同样的逻辑(假设最大值在 \([i, mid]\))中,……

然后再分治左右子区间。

示例程序

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;

int n, p[maxn], ans;

void cal(int l, int r) {
if (l == r) return;
int mid = (l + r) / 2;
cal(l, mid), cal(mid+1, r);
set<int> st;
for (int i = mid, j = mid+1, mx = p[j]; j <= r; mx = max(mx, p[++j])) {
while (i >= l && p[i] < mx) {
st.insert(p[i--]);
}
if (st.count(mx - p[j])) ans++;
}
st.clear();
for (int i = mid, j = mid+1, mx = p[i]; i >= l; mx = max(mx, p[--i])) {
while (j <= r && p[j] < mx) {
st.insert(p[j++]);
}
if (st.count(mx - p[i])) ans++;
}
}

int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", p+i);
cal(1, n);
printf("%d\n", ans);
return 0;
}