【JZOJ 省选模拟】冒泡的胖头鱼Out of Sorts

题目

Description
养鱼场里的胖头鱼在清晨习惯到水面冒泡。
说到冒泡就想到冒泡排序,所谓冒泡排序大约是这样一个东西

执行上面这个东西若干轮,直到数组有序。比冒泡排序更加快速的排序方法叫快速排序。
但养鱼场的管理员把这两种排序弄混了,变成一种新的排序方法,定义序列长度为n。
他先执行若干轮冒泡排序,直到在序列中出现一个分割点i,使得1…i的元素中最大值不大于i+1…n的元素中最小值。
养鱼场里有n条鱼,每条鱼每天早上会冒ai次泡,管理员想把他们按冒泡个数排序,于是用了如上的排序方法。
该方法写成代码形式大约如下:

管理员想知道他的工作量是多少,也就是执行完上述函数后work_counter的值,你能帮忙计算出来告诉管理员吗?

原题:
留意着农场之外的长期职业生涯的可能性,奶牛Bessie开始在不同的在线编程网站上学习算法。她最喜欢的两个算法是“冒泡排序”和“快速排序”,但是不幸的是Bessie轻易地把它们搞混了,最后实现了一个奇怪的混合算法! 如果数组 A 中 A [ . . . i ] 的最大值不大于 A [ i + 1 … ] 的最小值,我们就称元素 i 和 i + 1 之间的位置为一个“分隔点”。Bessie还记得快速排序包含对数组的重排,产生了一个分隔点,然后要递归对两侧的 A [ . . . i ] 和 A [ i + 1 … ] 排序。然而,尽管她正确地记下了数组中所有的分隔点都可以在线性时间内被求出,她却忘记快速排序应该怎么重排来快速构造一个分隔点了!在这个可能会被证明是排序算法的历史中最糟糕的算法性失误之下,她做出了一个不幸的决定,使用冒泡排序来完成这个任务。
以下是Bessie最初的对数组 A 进行排序的实现的概要。她首先写了一个简单的函数,执行冒泡排序的一轮:
bubble_sort_pass (A) {
for i = 0 to length(A)-2
if A[i] > A[i+1], swap A[i] and A[i+1]
}
她的快速排序(相当快)函数的递归代码是按下面的样子构成的:
quickish_sort (A) {
if length(A) = 1, return
do { // Main loop
work_counter = work_counter + length(A)
bubble_sort_pass(A)
} while (no partition points exist in A)
divide A at all partition points; recursively quickish_sort each piece
}
Bessie好奇于她的代码能够运行得多快。简单起见,她计算出她得主循环的每一轮都消耗线性时间,所以她相应增加一个全局变量work_counter的值,以此来跟踪整个算法总共完成的工作量。
给定一个输入数组,请预测quickish_sort函数接收这个数组之后,变量work_counter的最终值。

Input
第一行一个整数n,表示有n只胖头鱼
接下来n行,每行一个ai表示第i条胖头鱼的冒泡次数。

Output
一行一个整数,表示总的工作量

Sample Input
7
20
2
3
4
9
8
7

Sample Output
12

Data Constraint
1 ≤ N ≤ 100,000 0 ≤ ai ≤ 10^9
输入数据不保证各不相同。

Hint
在这个例子中,数组开始时为20 2 3 4 9 8 7。在一轮冒泡排序之后(增加7的工作量),我们得到2 | 3 | 4 | 9 8 7 | 20,其中|表示一个分隔点。于是我们的问题被分成了递归的子问题,包括对2、3、4、20排序(每个消耗0单元的工作量)和对9 8 7排序。对于9 8 7这个子问题,主循环的一轮(3单元工作量)得到8 7 | 9,在此之后最后一轮处理8 7(2单元工作量) 就有效地完成了排序。

思路

显然,如果一个序列排序完毕,每个数之间都会有一个分隔符号

我们考虑每一个数被排序了多少次
什么时候它不会参与冒泡排序,当且仅当它左右出现分割符,因此,我们只需要记录它左右出现分割符的时间的最大值
注意到我们冒泡排序每一次一定是将排名[1…i]和[i+1…n]归到两边,然后除开中间递归下去,并且冒泡排序不会跨过分隔符
那么对于第 i 个位置的分隔符,我只需要知道什么时候排名[1…i]被归到左边即可。
考虑冒泡排序的时候,第一次会把最大值放到最前面,然后次大值。
那么我们只需要知道排名[1…i]中最大的下标 x,然后 x-i 即是分隔符出现的时间,记得加 1。注意考虑相同的情况,这题就做完了。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+77;
struct A
{
	int val,id;
}a[N];
int n,t[N];
bool cmp(A a,A b)
{
	return (a.val<b.val)||(a.val==b.val&&a.id<b.id);
}
int main()
{
	scanf("%d",&n);
	for(int i=1; i<=n; i++) scanf("%d",&a[i].val),a[i].id=i;
	sort(a+1,a+1+n,cmp);
	int yjy=0;
	for(int i=1; i<=n; i++) 
	{
		yjy=max(yjy,a[i].id);
		t[i]=max(1,yjy-i);
	} 
	ll ans=0;
	for(int i=1; i<=n; i++) ans+=max(t[i],t[i-1]);
	printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值