[洛谷P4372][USACO18OPEN]Out of Sorts P(乱搞)

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值