[CSP-S 2026] 迁徙
大家好,我不喜欢 DP,所以场上对着这个 sb 玩意调了 3h
考虑这一类图的可达点是一段区间,对于每个点 \(u\) 我们求出 \([L_u, R_u]\) 表示可达范围。
每个双径对有恰好两条最短路径,我们发现其中一条就是 \(x \to R_x \to R_{R_x} \to\dots \to y\),除最后一步以外都跳 \(R_i\) 必然最优。设中间点为 \(\{a_i\}\),不妨令 \(a_0 = x\),则 \(\forall 1 \le i < dis, a_i = R_{a_{i - 1}}\)。
在这一条的基础上,显然的性质是另一条的中间点恰好有一个 \(a_i\) 不一样。考虑差的应该也不多,这个不一样的位置 \(a_i\) 必定是往前移动了 \(1\)(\(a_i' = a_i - 1\)),证明很显然,因为如果差了超过 \(1\),则考虑 \(z = a_i - 1\) 位置,有三条合法路径 \(a_{i-1} \to z \to a_{i+1}, a_{i-1} \to a_i \to a_{i+1}, a_{i-1} \to a_i' \to a_{i+1}\),超过 \(2\) 个路径。
性质分析得差不多了,考虑枚举这恰好一个不同的位置 \(j\),分析其前后两个位置 \(i, k\)。暂不考虑边界,假设 \(i, k\) 不是起点和终点。
枚举 \(i\),则 \(j = R_i, k = R_j\),钦定 \(i \leadsto k\) 有两种跳法,则跳法分别为 \(i \to j - 1 \to k, i\to j \to k\),所以 \(L_k = j - 1\)。考虑如果 \(L_{j - 1} < i\),则考虑 \(i\) 前一个位置的跳跃,可以直接跳至 \(j - 1\),再跳至 \(k\);\(L_j < i\) 也同理,只是 \(L_{j- 1}\) 更紧些。稍微写一下你会发现 wawawa。于是我对着这个东西调了 3h,如何呢。
开始实现:
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define drep(i, r, l) for (int i = r; i >= l; --i)
#define LL long long
#define FASTIO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define FRE(p) freopen(#p".in", "r", stdin), freopen(#p".out", "w", stdout)
#define eb emplace_back
#define ld lower_bound
#define ud upper_bound
#define PII pair<int,int>
using namespace std;
const int N = 2e6+5;
int n, nxt[N], pre[N];
LL ans[N], cnt[N];
LL d, a[N];
inline void solve() {
cin >> n >> d;
rep(i, 1, n) cin >> a[i];
rep(i, 1, n) nxt[i] = ud(a + 1, a + n + 1, a[i] + d) - a - 1, pre[i] = ld(a + 1, a + n + 1, a[i] - d) - a;
LL ret = 0;
drep(i, n, 1) {
int j = nxt[i], k = nxt[j];
cnt[i] = j - max(i - 1, nxt[i - 1]);
if (i < j && pre[j] == i) {
cnt[i] += cnt[j];
if (i < j - 1 && j < k && pre[k] == j - 1) {
if (pre[j - 1] == i) ans[i] += cnt[k] + 1;
else ret += cnt[k];
}
} else if (i < j - 1 && j < k && pre[k] == j - 1) ret += cnt[k];
}
int L = 1, R = 1;
rep(i, 1, n) {
int j = nxt[i];
while (L <= n && pre[L] < j - 1) ++L;
while (R <= n && pre[R] <= j - 1) ++R;
if (i < j - 1) {
if (pre[j] == i && pre[j - 1] == i) ans[i] += max(0, min(R, nxt[j]) - max(L - 1, j) - 1);
else ret += max(0, min(R - 1, nxt[j]) - max(L - 1, j));
}
}
drep(i, n, 1) {
ret += nxt[i] ^ i ? ans[nxt[i]] + ans[i] : ans[i];
if (i < nxt[i] && pre[nxt[i]] == i) ans[i] += ans[nxt[i]];
}
rep(i, 1, n) ans[i] = cnt[i] = 0;
cout << ret << '\n';
}
signed main() {
FASTIO;
#ifdef LOCAL
FRE(test);
#endif
int tid, _;
cin >> tid >> _;
while (_--) solve();
return 0;
}
小细节只需要对着 3,4 大样例调 3h+2h 就能写出来了,不是什么难点。不过也好,就算场上过了,我也刻不出来。

浙公网安备 33010602011771号