题解:
对于这种题还是要从部分分的做法下手。
如果操作都是[1…n],我们并不用关心具体的变化,每次都是取出最大值,然后插入一个值,显然用个堆就行了。
对于100分也是类似的,观察时限和数据范围,不难想到分块。
对于整块的,只要取出最大值,插入一个值,还要打上一个标记,表示插入个这个值。
因为在处理散块的时候,需要先把序列还原出来,那么就是标记如何下传的问题了。
首先不然想到标记之间的顺序没有关系,因为大的一定会在后面被小的覆盖,所以从左到右枚举,对于这个位置的值,与标记中最小的判断就行了。
常数感人。
Code:
#pragma GCC optimize(2)
#include<queue>
#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gc getchar
#define pc putchar
#define pb push_back
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 4e5 + 5, M = 650;
int n, Q, x, y, z, a[N], ii;
int bl[N], l[N / M + 50], r[N / M + 50], m;
priority_queue<int> q[N / M + 50], eq;
struct nod { int y; nod(int _y = 0) {y = _y;}};
bool operator <(nod a, nod b) { return a.y > b.y;}
priority_queue<nod> d[N], ed;
void sf(int x) {
fo(i, l[x], r[x]) {
if(d[x].empty()) break;
if(d[x].top().y < a[i]) d[x].push(nod(a[i])), a[i] = d[x].top().y, d[x].pop();
}
d[x] = ed;
}
int bins(int x, int y, int z) {
sf(bl[x]);
fo(i, x, y) if(a[i] > z) swap(a[i], z);
q[bl[x]] = eq;
fo(i, l[bl[x]], r[bl[x]])
q[bl[x]].push(a[i]);
return z;
}
int calc(int x, int y, int z) {
if(bl[x] == bl[y]) {
return bins(x, y, z);
} else {
z = bins(x, r[bl[x]], z);
fo(j, bl[x] + 1, bl[y] - 1) {
int p = q[j].top();
if(p > z) {
d[j].push(nod(z));
int z0 = z; z = p;
q[j].pop(); q[j].push(z0);
}
}
return bins(l[bl[y]], y, z);
}
}
void read(int &x) {
char c = ' '; x = 0;
while(c < '0' || c > '9') c = gc();
for(; c >= '0' && c <= '9'; c = gc()) x = x * 10 + c - 48;
}
void write(int x) {
if(!x) {pc('0'); return;}
int d[10]; d[0] = 0;
for(; x; x /= 10) d[++ d[0]] = x % 10;
while(d[0]) pc(d[d[0] --] + 48);
}
int main() {
freopen("in.in", "r", stdin);
freopen("in.out", "w", stdout);
scanf("%d %d", &n, &Q);
fo(i, 1, n) {
bl[i] = bl[i - 1];
if(i % M == 1) r[bl[i]] = i - 1, l[++ bl[i]] = i;
} r[m = bl[n]] = n;
fo(i, 1, n) read(a[i]);
fo(i, 1, n) q[bl[i]].push(a[i]);
for(ii = 1; ii <= Q; ii ++) {
read(x); read(y); read(z);
if(x <= y) write(calc(x, y, z)); else
write(calc(1, y, calc(x, n, z)));
pc('\n');
}
}