第一次写Splay的区间操作,调了5+小时…… Splay区间操作的核心就是区间的提取 例如:要提取[5, 12] 就要把4 Splay到根,把13 Splay到4的右儿子,那么13的左儿子就是[5, 12]的所有信息。
代码:
#include#define MAXN 200005int 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) 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); } }}