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。
Splay裸题。
//bzoj 1251 splay 复习
//支持操作为
//1: 区间+
//2: 区间翻转
//3: 区间求最大
#include <bits/stdc++.h>
using namespace std;
const int maxn = 50010;
const int maxm = 100010;
namespace SplayTree{
int siz[maxn], fa[maxn], ma[maxn], rev[maxn], lazy[maxn], val[maxn]; //siz代表节点的sz,fa代表父亲节点,ma代表最大值,rev翻转标记
//lazy 区间+, val 值
int ch[maxn][2], tot, root; //tot代表开新节点的时间戳,root代表splay的树根,ch[i][2],i的左右儿子
void newnode(int &rt, int father, int k){
rt = ++tot;
siz[rt] = 1, fa[rt] = father;
ma[rt] = val[rt] = k;
rev[rt] = lazy[rt] = ch[rt][0] = ch[rt][1] = 0;
}
void pushup(int rt){ //pushup 从底向上更新
siz[rt] = 1, ma[rt] = val[rt];
if(ch[rt][0] != 0) siz[rt] += siz[ch[rt][0]], ma[rt] = max(ma[rt], ma[ch[rt][0]]);
if(ch[rt][1] != 0) siz[rt] += siz[ch[rt][1]], ma[rt] = max(ma[rt], ma[ch[rt][1]]);
}
void pushdown(int rt){ //pushdown 从上向下更新
if(!rt) return;
if(lazy[rt]){ //区间+懒惰标记传递
int l = ch[rt][0], r = ch[rt][1];
if(l != 0) ma[l] += lazy[rt], val[l] += lazy[rt], lazy[l] += lazy[rt];
if(r != 0) ma[r] += lazy[rt], val[r] += lazy[rt], lazy[r] += lazy[rt];
lazy[rt] = 0;
}
if(rev[rt]){ //区间翻转懒惰标记传递
int l = ch[rt][0], r = ch[rt][1];
if(l != 0) rev[l] ^= 1, swap(ch[l][0], ch[l][1]);
if(r != 0) rev[r] ^= 1, swap(ch[r][0], ch[r][1]);
rev[rt] ^= 1;
}
}
void rotate(int x){ //旋转,把x转到根节点,kind为1代表右旋,kind为0代表左旋
int y = fa[x], kind = ch[y][1] == x;
pushdown(y), pushdown(x);
ch[y][kind] = ch[x][!kind];
fa[ch[y][kind]] = y;
ch[x][!kind] = y;
fa[x] = fa[y];
fa[y] = x;
ch[fa[x]][ch[fa[x]][1] == y] = x;
pushup(y), pushup(x);
}
void splay(int x, int goal) //伸展操作,把根为r的子树调整为goal
{
while(fa[x] != goal)
{
int y = fa[x], z = fa[y];
if(z == goal) rotate(x);
else if((ch[y][1] == x) == (ch[z][1] == y)) rotate(y), rotate(x);
else rotate(x), rotate(x);
}
if(goal == 0) root = x;
}
void build(int &rt, int l, int r, int father){ //建立一颗空的splaytree
if(l > r) return;
int mid = (l + r) / 2;
newnode(rt, father, 0); //递归申请新节点
build(ch[rt][0], l, mid - 1, rt);
build(ch[rt][1], mid + 1, r, rt);
pushup(rt);
}
int find(int x, int k){ //查找第k位置在splaytree中的位置
pushdown(x);
int lsum = siz[ch[x][0]] + 1;
if(lsum == k) return x;
else if(lsum < k) return find(ch[x][1], k - lsum);
else return find(ch[x][0], k);
}
void update_val(int l, int r, int v){ //区间更新+
splay(find(root, l), 0);
splay(find(root, r + 2), root);
lazy[ch[ch[root][1]][0]] += v;
ma[ch[ch[root][1]][0]] += v;
val[ch[ch[root][1]][0]] += v;
}
void update_rev(int l, int r){
splay(find(root, l), 0);
splay(find(root, r + 2), root);
int tmp = ch[ch[root][1]][0]; //现在以tmp为根的splaytree就是l,r区间
rev[tmp] ^= 1;
swap(ch[tmp][0], ch[tmp][1]);
}
int querymax(int l, int r){ //查询区间最大值
splay(find(root, l), 0);
splay(find(root, r + 2), root);
return ma[ch[ch[root][1]][0]];
}
}
using namespace SplayTree;
int main(){
int n, m;
scanf("%d%d", &n, &m);
newnode(root, 0, -1);
newnode(ch[root][1], root, -1);
build(ch[ch[root][1]][0], 1, n, ch[root][1]);
for(int i = 1; i <= m; i++){
int cmd, a, b, c;
scanf("%d", &cmd);
if(cmd == 1){
scanf("%d%d%d", &a, &b, &c);
update_val(a, b, c);
}else if(cmd == 2){
scanf("%d%d", &a, &b);
update_rev(a, b);
}else{
scanf("%d%d", &a, &b);
printf("%d\n", querymax(a, b));
}
}
return 0;
}