传送门
第一次写Splay的区间操作,调了5+小时……
Splay区间操作的核心就是区间的提取
例如:要提取[5, 12] 就要把4 Splay到根,把13 Splay到4的右儿子,那么13的左儿子就是[5, 12]的所有信息。
代码:
#include<cstdio>
#define MAXN 200005
int tmp;
inline void Swap(int &a, int &b) { tmp = a; a = b; b = tmp; }
struct node {
int sz, ch[2], f, v, mn, dv; bool rev; //mn:子树中的最小值,rev:是否经过翻转, dv:delta v,区间增减值的懒标记
inline void mt() {Swap(ch[0], ch[1]);}
}t[MAXN];
int rt, sz, n, m;
inline void pushdown(int y) {
if(t[y].rev) {t[y].mt(); if(t[y].ch[0]) t[t[y].ch[0]].rev ^= 1; if(t[y].ch[1]) t[t[y].ch[1]].rev ^= 1; t[y].rev = 0;}
if(t[y].dv != 0) {t[y].v += t[y].dv; if(t[y].ch[0]) t[t[y].ch[0]].dv += t[y].dv; if(t[y].ch[1]) t[t[y].ch[1]].dv += t[y].dv; t[y].dv = 0;}
}
inline int Min(int a, int b) {return a < b ? a : b;}
inline void Upd(int r) {
t[r].sz = t[t[r].ch[0]].sz + t[t[r].ch[1]].sz + 1;
t[r].mn = Min(t[t[r].ch[0]].mn + t[t[r].ch[0]].dv, Min(t[t[r].ch[1]].mn + t[t[r].ch[1]].dv, t[r].v + t[r].dv));
}
inline void rot(int x)
{
int y = t[x].f, z = t[y].f;
bool f = (t[y].ch[1] == x);
pushdown(y);pushdown(x);
t[y].ch[f] = t[x].ch[f^1];
if(t[y].ch[f]) t[t[y].ch[f]].f = y;
t[x].ch[f^1] = y; t[y].f = x;
t[x].f = z;
if(z) t[z].ch[t[z].ch[1]==y] = x;
Upd(y);
}
void Splay(int r, int tp, int &Root = rt) {
for(int y, z; (y = t[r].f) != tp; rot(r)) {
pushdown(r);
z = t[y].f;
if(z == tp) continue;
if( (t[z].ch[0] == y) == (t[y].ch[0] == r) ) rot(y);
else rot(r);
}
if(!tp) Root = r; Upd(r);
}
int Kth(int x, int &Root = rt)
{
int y = Root, p;
if(x>t[Root].sz)return 0;
while(1)
{
pushdown(y); Upd(y);
p=t[y].ch[0];
if(t[p].sz+1<x) {
x-=t[p].sz+1;
y=t[y].ch[1];
}
else if(t[p].sz>=x) y=p;
else break;
}
Splay(y, 0, Root);
return y;
}
int a[MAXN];
int Build(int l, int r) { //把初始区间建成二叉树
if(l > r) return 0;
int mid = (l + r) >> 1;
t[mid].ch[0] = Build(l, mid-1);
t[mid].ch[1] = Build(mid+1, r);
t[mid].v = t[mid].mn = a[mid];
Upd(mid); if(t[mid].ch[0]) t[t[mid].ch[0]].f = mid; if(t[mid].ch[1]) t[t[mid].ch[1]].f = mid;
return mid;
}
inline void Rev(int l, int r) { //区间翻转
int b = Kth(r+2), a = Kth(l);
Splay(b, a); int p = t[b].ch[0];
t[p].rev ^= 1;
}
inline void Add(int l, int r, int w) {//区间增加
int b = Kth(r+2), a = Kth(l);
Splay(b, a); int p = t[b].ch[0];
t[p].dv += w; Upd(b); Upd(a);
}
inline int QMIN(int l, int r) {//区间最小值
int b = Kth(r+2), a = Kth(l);
Splay(b, a); int p = t[b].ch[0]; Upd(b); Upd(a);
return t[p].mn + t[p].dv; //一定要加t[p].dv!!!
}
inline void Merge(int &left, int right) {//区间合并
int p = rt;
while(t[p].ch[1]) p = t[p].ch[1];
Splay(p, 0, left);
t[left].ch[1] = right; t[right].f = left;
Upd(left);
}
inline void split(int &x, int k, int &left, int &right) {//区间分离
Kth(k, x); left = x;
right = t[x].ch[1]; t[right].f = 0;
t[x].ch[1] = 0;
Upd(left);
}
void Revolve(int l, int r, int T) //题目中定义的Revolve操作
{
T = T%(r-l+1)+(r-l+1); //discuss里面说T可能为负数……
T %= (r-l+1);
if (!T) return;
Rev(l, r); Rev(l, l+T-1);
Rev(l+T, r);
}
void Del(int k)
{
int t1, t2, t3;
split(rt, k, t1, t2);
split(t2, 1, t2, t3);
Merge(t1, t3);
rt = t1;
}
char c, f;
inline void GET(int &n) {
n = 0; f = 1;
do {c = getchar(); if(c == '-') f = -1;} while(c > '9' || c < '0');
while(c >= '0' && c <= '9') {n=n*10+c-'0';c=getchar();}
n *= f;
}
void Ins(int k, int x)
{
++ sz;
t[sz].v = t[sz].mn = x;
t[sz].sz = 1; int t1, t2;
split(rt, k+1, t1, t2);
Merge(t1, sz);
Merge(t1, t2);
rt = t1;
}
int main() {
int t1, t2, t3;
GET(n); a[1] = a[n+2] = ~0U>>2;
t[0].mn = ~0U>>2;
for(int i = 1; i <= n; i ++) GET(a[i+1]);
rt = Build(1, n+2); sz = n+4;
GET(m); char s[10];
while(m --) {
scanf("%s", s);
if(s[0] == 'A') {
GET(t1); GET(t2); GET(t3);
Add(t1, t2, t3);
}
else if(s[0] == 'M') {
GET(t1); GET(t2);
printf("%d\n", QMIN(t1, t2));
}
else if(s[0] == 'I') {
GET(t1); GET(t2); Ins(t1, t2);
}
else if(s[0] == 'D') {
GET(t1); Del(t1);
}
else if(s[3] == 'E') {
GET(t1); GET(t2);
Rev(t1, t2);
}
else {
GET(t1); GET(t2); GET(t3);
Revolve(t1, t2, t3);
}
}
}