题目:
给定序列 (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;
}