Description
网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道题目 就叫序列终结者吧。 【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。
Input
第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。
Output
对于每个第3种操作,给出正确的回答。
Sample Input
4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4
Sample Output
2
【数据范围】
N<=50000,M<=100000。
Solution
裸题...区间反转打标记(标记方式是^1,然后交换左右子树就好了(必须是按位置分裂的fhqtreap)),区间加也打标记(类似线段树那样),split和merge的时候down一下就好了,注意要将root的val和mx设为-inf(保护节点)不然会锅。
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define lc (t[rt].l)
#define rc (t[rt].r)
const int inf = 1e9;
int n, m, root, tot;
struct fhq { int flag, l, r, siz, rnd, val, mx, tag; } t[N];
void up(int rt) {
if(!rt) return;
t[rt].siz = t[lc].siz + t[rc].siz + 1;
t[rt].mx = max(t[rt].val, max(t[lc].mx, t[rc].mx));
}
void rev(int rt) { t[rt].flag ^= 1; swap(lc, rc); }
void addone(int rt, int c) { t[rt].tag += c; t[rt].mx += c; t[rt].val += c; }
void down(int rt) {
if(!rt) return;
if(t[rt].tag) addone(lc, t[rt].tag), addone(rc, t[rt].tag);
if(t[rt].flag) rev(lc), rev(rc);
t[rt].tag = t[rt].flag = 0;
}
void split(int rt, int &l, int &r, int k) {
if(!k) l = 0, r = rt;
else if(k == t[rt].siz) l = rt, r = 0;
else if(k <= t[lc].siz) down(r=rt), split(lc, l, lc, k), up(rt);
else down(l=rt), split(rc, rc, r, k - t[lc].siz - 1), up(rt);
}
void merge(int &rt, int l, int r) {
if(!l || !r) rt = l + r;
else if(t[l].rnd < t[r].rnd) down(rt=l), merge(rc, rc, r), up(rt);
else down(rt=r), merge(lc, l, lc), up(rt);
}
int build(int v) {
t[++tot].rnd = rand()<<15|rand();
t[tot].mx = t[tot].val = v; t[tot].siz = 1;
t[tot].l = t[tot].r = t[tot].flag = t[tot].tag = 0;
return tot;
}
void rever(int l, int r) {
int x, y, tmp;
split(root, x, y, r); split(x, x, tmp, l - 1);
rev(tmp);
merge(x, x, tmp); merge(root, x, y);
}
void add(int l, int r, int c) {
int x, y, tmp;
split(root, x, y, r); split(x, x, tmp, l - 1);
addone(tmp, c);
merge(x, x, tmp); merge(root, x, y);
}
int query(int l, int r) {
int x, y, tmp;
split(root, x, y, r); split(x, x, tmp, l - 1);
int ans = t[tmp].mx;
merge(x, x, tmp); merge(root, x, y);
return ans;
}
int main() {
srand((unsigned)time(0)); scanf("%d%d", &n, &m);
t[0].mx = t[0].val = -inf;
for(int i = 1; i <= n; ++i) merge(root, root, build(0));
for(int i = 1; i <= m; ++i) {
int op, l, r, v;
scanf("%d%d%d", &op, &l, &r);
if(op == 1) {
scanf("%d", &v);
add(l, r, v);
} else if(op == 2) rever(l, r);
else printf("%d\n", query(l, r));
}
return 0;
}