传送门嗖啪
题目大意
(自己看题吧)
思路
可以画出来,连续的同一个方向的操作可以合并在一起,只取最大的那一个,
但是还有后面的比前边的大的可能,这时候就会发现,如果后面一个比前面的范围大的话,就要去掉中间的操作,包括去掉那个范围小的,详细的解说放在了下面代码中,大家自行观看,上代码!
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define sc(a) scanf("%lld",&a)
#define pf(a) printf("%d",a)
#define endl "\n"
#define int long long
#define x first
#define y second
#define pii pair<int, int>
const int N = 100010;
int n, m, p, q;
pii stk[N];//这是个栈,用来存储我们所有的操作
/*
第一个关键字表示是什么操作,是前缀还是后缀
第二个关键字是存下标是什么
*/
int ans[N];//存一下答案
signed main()
{
sc(n);sc(m);
int top = 0;//栈顶元素
for(int i=1;i<=m;i++)
{
sc(p);sc(q);
if(!p)//p是0,表示是前缀操作
{
//对于连续的前缀操作,只保留一个最大的
//栈是不空的,栈顶元素是前缀操作的话 ,保留一个最大的
while(top && stk[top].x==0) q=max(q, stk[top--].y);
/*
如果前缀和是不连续的话, 要删掉前面所有长度比当前小的前缀操作
*/
while (top >= 2 && stk[top - 1].y <= q) top -= 2;
stk[ ++ top] = {0, q};
}
else if (top)//第一个操作不能是后退操作,第一个操作是后退操作没有意义
{
//判断一下栈非空 ,并且栈顶的操作跟我们现在的操作一致
//这里top不能去掉,因为后面的min函数中有top--
while (top && stk[top].x == 1) q = min(q, stk[top -- ].y);
/*这里一定是同类操作比较y,因为上一步top--,所有这里top
指向了stk[top].x=0的栈顶*/
while (top >= 2 && stk[top - 1].y >= q) top -= 2;
stk[ ++ top] = {1, q};
}
}
/*
倒着填所有的数,本题就相当于最边上的数不变,然后
边上不变的数的个数依次增多
*/
int k = n, l = 1, r = n;
for (int i = 1; i <= top; i ++ )
{
if (stk[i].x == 0)
while (r > stk[i].y && l <= r) ans[r -- ] = k -- ;
else
while (l < stk[i].y && l <= r) ans[l ++ ] = k -- ;
if (l > r) break;
}
//这时可能所有的并没有完全排序
/*
如果top是奇数,说明刚刚完成了一次左边,
没有被排到的也就在左边被排序,也就是逆序,l++
从大到小,从左开始排
*/
if (top % 2)
while (l <= r) ans[l ++ ] = k -- ;
else
while (l <= r) ans[r -- ] = k -- ;
/*
偶数说明刚刚完成右操作,顺序,也就是从右到左得逆序,r--
*/
for (int i = 1; i <= n; i ++ )
printf("%d ", ans[i]);
return 0;
}