[蓝桥杯 2021 省 B] 双向排序

题目:

给定序列 (a1​,a2​,⋯,an​)=(1,2,⋯,n),即ai​=i 。

小蓝将对这个序列进行 m 次操作,每次可能是将 a1​,a2​,⋯,aqi​​ 降序排列,或者将 aqi​​,aqi​+1​,⋯,an​ 升序排列。

请求出操作完成后的序列。

蒻蒟第一次写博客,有错误希望多多指教

思路参考:P8747 [蓝桥杯 2021 省 B] 双向排序 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题的意思就是有 n 个数,是从 1 到 n,进行 m 次操作,每次操作输入一个 p 和 q,p 是 0 的时候把 1 到 q 降序排,p 是 1 的时候把 q 到 n 升序排。如果我们直接sort排序,时间复杂度会达到(nmlogn),显然会超时。

我们分析可以发现,在无脑排序的过程中,我们进行了许多无效操作。设 x < y,我们先对[1,x]降序排序,再对 【1,y】降序排序,我们发现对【1,y】的降序排序覆盖了对【1,x】的降序排序,所以我们可以去掉对【1,x】的排序,取范围最大的降序排序。升序同理。

所以我们可以发现去掉无效的排序后,降序排序和升序排序是交替进行的。还有由于本身序列是升序排序,所以第一次有效排序一定是降序排序。

现在我们就要考虑两种情况,一是要降序排序,另一个就是要升序排序。先来考虑降序排序,设本次降序排序为【1,a】,降序排序之前的升序排序为【b,n】,a>b,由于最开始序列为升序,所以【a,n】中的任意元素 > 【1,a】中任意元素。设上一次降序排序为【1,c】(b<c<a),同理,【c,n】>【1,c】,因为b<c,所以【1,b】<c< a,对【1,a】降序,相当于覆盖了对【1,c】降序和对【b,n】升序两次操作。 

举个例子: 1 2 3 4 5 6 7 8 9 ,我们先对1 2 3降序排 -> 3 2 1 4 5 6 7 8 9,再对2-9升序排,

             1-3降序:3 2 1 4 5 6 7 8 9

             2-9升序:3 1 2 4 5 6 7 8 9

             1-6降序:6 5 4 3 2 1 7 8 9

可以发现直接对1 2 3 4 5 6 7 8 9,进行1-6降序 ,得到 6 5 4 3 2  1 7 8 9

结果是一样的

当操作是升序的时候,我们考虑 降序【1-d】-升序【c-n】-降序【1-b】-升序(本次操作) 【a-n】,a < c<b<d,注意如果这里b>d的话就是第一种情况了,对【1-b】的降序操作覆盖了对【1-d】的降序和对【c-n】的升序。   对【1-d】的降序操作已经将【1-d】中的最大元素放到了a前面,此后两次操作不会改变【a,n】中的元素,只会改变其排序,所以相当于对【a-n】升序操作覆盖了对【c-n】升序和对【1-b】降序

例子: 1 2 3 4 5 6 7 8 9

 1-6降序:6 5 4 3 2 1 7 8 9  

4-n升序: 6 5 4 1 2 3 7 8 9

1-5降序:6 5 4 2 1 3 7 8 9

2-n升序  6 1 2 3 4 5 7 8 9

可见直接对1-6降序操作后的序列进行2-n升序操作得到6 1 2 3 4 5 7 8 9,结果是一样的。

好了现在逻辑部分理清了,那么代码怎么写呢?我们用到了之前的序列状态,想一想这和什么数据结构有关呢?当然是栈结构啦。

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
pair<int, int> Stack[N]; //第一个元素表示升序还是降序,第二个元素表示操作范围
int ans[N];
void solve()
{
    int n,m;
    cin >> n>>m;
    int top = 0;
    while (m--)
    {
        int p, q;
        cin >> p >> q;
        if (!p)
        {
            //去除无效操作
            while (top && Stack[top].first == 0)
            {
                q = max(q, Stack[top--].second);
            }
            //去除被覆盖的操作,注意此时栈顶存放的是上一次操作,本次操作还没有入栈
            while (top >= 2 && Stack[top - 1].second <= q)
            {
                top -= 2;
            }
            Stack[++top] = { 0,q };
        }
        else if (top)
        {
            while (top && Stack[top].first == 1)
            {
                q = min(q, Stack[top--].second);
            }
            while (top >= 2 && Stack[top - 1].second >= q)
            {
                top -= 2;
            }
            Stack[++top] = { 1,q };
        }
    
    }
    int k = n, l = 1, r = n;
    //构建输出数组,先构建升序和降序的非交集部分
    for (int i = 1; i <= top; i++)
    {
        if (Stack[i].first == 0)
        {
            while (l<=r && r > Stack[i].second)
            {
                ans[r--] = k--;
            }
        }
        else
        {
            while (l <= r && l < Stack[i].second)
            {
                ans[l++] = k--;
            }
        }
        if (l > r)
        {
            break;
        }
    }
    //构建交集部分,top为奇数时,交集部分为降序,偶数时为升
    if (top % 2)
    {
        while (l <= r)
        {
            ans[l++] = k--;
        }
    }
    else
    {
        while (l <= r)
        {
            ans[r--] = k--;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i] << " ";
    }
}
int main()
{
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值