2025/3/17
比赛场次:http://www.nfls.com.cn:20035/contest/1691
总用时:40min
通过:ABCD
未通过:E
E
题目描述
对于一个排列 \(p\),定义其价值为满足 \(p_i = i\) 的位置个数。
给定一个长度为 \(n\) 的序列 \(p\),现在要求把其中一段翻转,使得其价值最大。输出最优决策下相对于原序列增加的权值。
\(n \le 5 \times 10^5\)。
Solution
首先计算原排列中的固定点数目,使用前缀和数组 \(s\),其中 \(s_i\) 表示前 \(i\) 个元素中的固定点数目。
对于每个可能的翻转区间 \([l, r]\),令 \(s = l+r\)。翻转后,原位置 \(i\) 的元素会被移动到位置 \(s-i\)。若翻转后的位置 \(j\) 满足 \(p_j = j\),则原元素必须满足 \(p_i = s-i\),即 \(p_i+i = s\)。因此,所有满足该条件的元素可能成为翻转后的固定点。
将满足 \(p_i+i = s\) 的元素 \(i\) 存入向量 \(vt_s\) 中。这样,每个 \(s\) 对应可能的翻转区间 \([l, r]\),其中 \(l + r = s\)。
通过枚举所有可能的 \(s\) 和对应的 \(j\),找到最大增加的权值,即为所求结果。其中对于每个 \(s\),遍历 \(vt_s\) 中的元素 \(j\),计算翻转区间 \([\min(j, s-j), \max(j, s-j)]\) 带来的增加的权值。该增加的权值等于新增的固定点数目(即满足条件的元素数目)减去原区间内的固定点数目。时间复杂度为 \(O(n \log n)\)。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 5e5 + 7;
LL s[N<<1];
vector<LL> vt[N<<1];
int main() {int n;scanf("%d", &n);for (int i = 1; i <= n; ++i) {LL x;scanf("%lld", &x);vt[x+i].push_back(i);s[i] = s[i-1] + (x==i);}LL ans = 0;for (int i = 2; i <= n<<1; ++i)for (auto j : vt[i])ans = max(ans, upper_bound(vt[i].begin(), vt[i].end(), max(j, i-j))-lower_bound(vt[i].begin(), vt[i].end(), min(j, i-j))-s[max(j, i-j)]+s[min(j, i-j)-1]);printf("%lld", ans);return 0;
}
2025/3/21
比赛场次:http://www.nfls.com.cn:20035/contest/1694
总用时:1h
通过:
未通过: