[USACO18OPEN] Out of Sorts P 题解

前言

题目链接:洛谷

其他题解十分生硬地给出了 t[] 的定义,本题解按照解题思路,层层递进,自然得出题目解法。

题意简述

以下是冒泡排序的一轮:

void bubble_sort(int val[], int l, int r) {
for (int i = l; i < r; ++i)
if (val[i] > val[i + 1])
swap(val[i], val[i + 1]);
}

定义 (p,p+1){an} 的一个「分割点」当 maxi=1paimini=p+1nai

以下是一个基于分治和冒泡排序的排序算法:

void qsort(int val[], int l, int r){
if (l == r) return;
do {
bubble_sort(val, l, r);
work_cnt += r - l + 1;
} while (!check(val, l, r));
// check(val, l, r) 返回 val[l~r] 中是否存在「分割点」
divide_and_qsort_each_piece(val, l, r);
// 将 val[l~r] 按照「分割点」分割成若干子问题,并递归调用 qsort
}

给你长度为 n 的序列 {an},求运行 qsort(a, 1, n) 后,全局变量 work_cnt 的值。

n105

题目分析

显然我们需要分析冒泡排序的性质。

一轮冒泡排序后,肯定会产生一个「分割点」。这是由于一轮冒泡排序后,序列中的最大值被冒泡到了最右侧,所以产生了一个「分割点」。所以那个 do...while 实际没有用处。

然后发现整个序列在排序过程中比较鬼畜,难以发现性质。于是考虑转变计数视角,求每一个元素被 bubble_sort 了多少次,答案就是每个元素答案之和。

对于一个元素,只要它没有到达最终位置,它会一直被 bubble_sort,这在时间轴上体现为一段前缀,我们只要求出在什么时候这个元素不会被 bubble_sort 即可。发现,每个元素唯一的递归出口 l == r 的实际含义为,该元素左右两侧都出现了「分割点」,于是问题似乎可以被进一步规约到每一个「分割点」出现的时刻。

一个「分割点」出现,当该在它左侧的元素都在它左侧,该在它右侧的元素都在它右侧了。这么想是因为我们冒泡排序的经典思考方式,关注于每一个元素的移动。我们有一个经典结论:

经典结论:

对于一个元素,倘若它想要向左移动,它在一轮冒泡中会恰向左移动一个位置。

考虑在一轮冒泡中,它会且只会和其左侧的最大值 swap,然后向左移动一个位置。

所以我们考虑在某一个「分割点」右侧的元素,什么时候到这个「分割点」左侧。所需的冒泡轮数,根据我们的结论,就是它到「分割点」的距离。那么对于所有在它右侧且想要跑到它左侧的元素,求出位置最靠右的元素的位置,即可求出这个「分割点」出现的时间。

接下来随便求了。给出一种可能的实现方式:设 bi 表示排序后的序列 a 的第 i 个元素 ai 为原序列 abi,再设 mxRi 表示 1i 中,目标位置最右在哪里。那么 ti=mxRii 就是「分割点」(i,i+1) 出现的时刻。倘若其值小于 1,说明该「分割点」一开始便存在,对 1max 即可。答案根据我们的分析为 i=1nmax{ti1,ti},其中 t0=1bi 排序一下即可,很好求,不难发现 mxRi 即为 bi 的前缀最大值。

时间复杂度 O(nlogn),瓶颈在于一开始的排序。

代码

实际实现起来很短。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, a[N], b[N], t[N];
long long ans;
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
b[i] = i;
}
sort(b + 1, b + n + 1, [] (int x, int y) -> bool {
if (a[x] == a[y]) return x < y; // notice this detail
return a[x] < a[y];
});
t[0] = 1;
for (int i = 1; i <= n; ++i) {
b[i] = max(b[i], b[i - 1]); // mxR[i]
t[i] = b[i] - i;
if (t[i] <= 0) t[i] = 1;
}
for (int i = 1; i <= n; ++i)
ans += max(t[i], t[i - 1]);
printf("%lld", ans);
return 0;
}
posted @   XuYueming  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示