CodeForces-631C Report 单调栈,思维
CodeForces-631C Report 单调栈,思维
题意
给定一个初始序列\(a\)
输出经过\(m\)次操作后的序列
每个操作是两种之一
- \(1 \quad r\) ,将序列\([1,r]\) 从小到大排序
- \(2\quad r\) ,将序列\([1,r]\)从大到小排序
分析
暴力显然是不可取的。
注意到性质
对于操作\(i,j\),若$i > j \(且\)r_i > r_j$ 那么这个\(j\)是无效的。
这提示我们利用这个性质缩小范围。
根据\(r\)维护一个单调递减的单调栈。每次我们只需要考虑两个相邻的栈的\(r\)之间的元素。
并且由于之前最大的\(r\)已经把\([1,r]\)排好序了。接下来每次都只是用双指针选出排好序中连续的一段。
这样就可以\(O(nlogn)\)实现,瓶颈在于排序。
代码
int a[200005];
int b[200005];
int c[200005];
int op[200005];
int st[200005];
int main() {
int n = readint();
int m = readint();
int ptr = 0;
for (int i = 0; i < n; i++)
a[i] = readint();
for (int i = 0; i < m; i++) {
int tmp = readint();
c[i] = readint();
while (ptr && c[st[ptr - 1]] < c[i])
ptr--;
st[ptr] = i, op[ptr++] = tmp;
}
int pos = c[st[0]];
for (int i = 0; i < pos; i++)
b[i] = a[i];
sort(b, b + pos);
int l = 0;
int r = c[st[0]];
for (int i = 1; i < ptr; i++) {
for (int j = c[st[i - 1]]; j > c[st[i]]; j--)
a[j - 1] = (op[i - 1] == 1) ? b[--r] : b[l++];
}
if (ptr - 1 >= 0) {
for (int i = c[st[ptr - 1]]; i > 0; i--)
a[i - 1] = (op[ptr - 1] == 1) ? b[--r] : b[l++];
}
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
}