https://www.luogu.org/problemnew/show/P4372
模拟赛的题,考场上看着是最可做的。洛谷评分是NOI+/CTSC是smg…
依旧是放我们模拟赛的题意:
世界上OI水平最高的奶牛bessie正在学习排序算法。她先后学习了冒泡排序和快速排序。众所周知,快排将序列分成若干段使得前一段的所有数都
≤
≤
后一段的所有数。我们称两段之间的分界点为分隔点。
聪明的bessie打算把快速排序和冒泡排序结合起来,并验证一下它的时间复杂度,写出来如下伪代码:
bubble_sort_pass (A) {
for i = 0 to length(A)-2
if A[i] > A[i+1], swap A[i] and A[i+1]
}
bessie_sort (A) {
if length(A) = 1, return
do {
work_counter = work_counter + length(A) // 计算复杂度
bubble_sort_pass(A)
} while (A中没有分隔点)
把A从分隔点分开,递归执行每一段
}
给你一个长度为n(n<=10^5)的序列,你要求出用 bessie_sort 算法排序的时间复杂度(即 work_counter 的值)
题解写的太好了,%tkj直接转题解里面的话好了:
首先,如果不考虑work_counter的话,这个算法相当于对序列进行冒泡排序,因为冒泡排序中不会有元素穿过分隔点。
work_counter每次加的是当前的长度,也就是说当前每个元素会对答案产生1的贡献。一个元素会一直产生贡献直到它两边都有分隔点。
如果我们求出了每个分隔点出现的时间,那么每个点的贡献就是两边分割点出现时间的较大值。
考虑怎么算i和i+1间出现分隔点的时间:出现分隔点当且仅当前前i小的数全部在分隔点之前。一轮冒泡过后前 个数中最靠右的数一定会向左移一位,所以出现的时间就是 r - i 。
具体实现的话,我们可以先把所有元素排序(先按大小,再按原序列中的位置,因为冒泡排序是稳定排序),从左往右求出现时间,同时用第i小的元素的初始位置维护当前最靠右的位置。最后用上文提到的方法求出答案就好了。
注意:n=1的时候要特殊处理,因为根本不会进入循环;如果一个位置两边一开始就有分隔点,它也会产生1的贡献,因为这是个dowhile循环。复杂度瓶颈在于排序,总复杂度O(NlogN)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
int x,id;
}a[100010];
bool cmp(node x,node y)
{
if(x.x==y.x) return x.id<y.id;
else return x.x<y.x;
}
int t[100010];
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].id=i;
}
if(n==1){puts("0"); return 0;}
sort(a+1,a+n+1,cmp);
int maxx=0;
for(int i=1;i<n;i++)
{
maxx=max(maxx,a[i].id);
t[i]=maxx-i;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
int tmp=0;
if(i>=1) tmp=max(tmp,t[i-1]);
if(i< n) tmp=max(tmp,t[i]);
if(tmp==0) tmp=1;
ans+=tmp;
}
printf("%lld\n",ans);
return 0;
}