首先给出一论文讲的很好:
http://www.docin.com/p-63165342.html
http://www.docin.com/p-62465596.html
然后给出模板胡浩大神的模板:http://www.notonlysuccess.com/index.php/splay-tree/
好像胡浩大神的没有给注释,然后给出cxlove的,给出了详细的注释:
http://blog.csdn.net/acm_cxlove/article/details/7790895
然后给出模板题目:
题意:中文..
思路:
每输入一个值,我们就更新splay树,然后找他的左子树中最靠右的,右子树中最靠左的那肯定和他的距离差值是最小的。然后求和即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d ", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 32777 using namespace std; const int inf = 0x7f7f7f7f; const ll mod = 1000000007; struct SplayTree { int chd[N][2],pre[N],key[N]; int root,tot; //生成新节点 void newNode(int &r,int fa,int k) { r = ++tot; pre[r] = fa; key[r] = k; chd[r][0] = chd[r][1] = 0; } //旋转 void Rotate(int x,int kd) { int y = pre[x]; chd[y][!kd] = chd[x][kd]; pre[chd[x][kd]] = y; if (pre[y] != 0) chd[pre[y]][chd[pre[y]][1] == y] = x; pre[x] = pre[y]; pre[y] = x; chd[x][kd] = y; } // void Splay(int r,int g) { while (pre[r] != g) { if (pre[pre[r]] == g) Rotate(r,chd[pre[r]][0] == r); else { int y = pre[r]; int kd = chd[pre[y]][0] == y; if (chd[pre[r]][kd] == r) { Rotate(r,!kd); Rotate(r,kd); } else { Rotate(y,kd); Rotate(r,kd); } } } if (g == 0) root = r; } //插入 int insert(int k) { int r = root; while (chd[r][key[r] < k]) { if (key[r] == k) { Splay(r,0); return 0; } r = chd[r][key[r] < k]; } newNode(chd[r][key[r]< k],r,k); Splay(chd[r][key[r] < k],0); return 1; } int get_next(int x) { int rg = chd[x][1]; if (rg == 0) return inf; while (chd[rg][0]) rg = chd[rg][0]; return key[rg] - key[x]; } int get_pre(int x) { int lt = chd[x][0]; if (lt == 0) return inf; while (chd[lt][1]) lt = chd[lt][1]; return key[x] - key[lt]; } }spt; int n; int main() { // Read(); int x; while (~scanf("%d",&n)) { spt.tot = spt.root = 0; int ans = 0; for (int i = 1; i <= n; ++i) { scanf("%d",&x); // cout<< ">>>>" << x << endl; if (i == 1) { ans += x; spt.newNode(spt.root,0,x); } else { if (spt.insert(x) == 0) continue; int a = spt.get_next(spt.root); int b = spt.get_pre(spt.root); ans += min(a,b); } // cout << ans << endl; } printf("%d ",ans); } return 0; }
线段树可解决的问题,Splay解决:
pku 3468 A Simple Problem with Integers
成段更新,区间询问
第二个论文里面有提到如何通过Splay的操作来实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d ", x) #define lowbit(x) (x)&(-x) #define keyTree (chd[chd[root][1]][0]) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 200007 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; struct SpayTree { public: int sz[N]; int ss[N],que[N]; int chd[N][2],pre[N]; int top1,top2,root; inline void Rotate(int x,int kd) { int y = pre[x]; pushdown(y); pushdown(x); chd[y][!kd] = chd[x][kd]; pre[chd[x][kd]] = y; pre[x] = pre[y]; if (pre[x]) chd[pre[y]][chd[pre[y]][1] == y] = x; chd[x][kd] = y; pre[y] = x; pushup(y); } inline void Splay(int x,int goal) { pushdown(x); while (pre[x] != goal) { if (pre[pre[x]] == goal) { Rotate(x,chd[pre[x]][0] == x); } else { int y = pre[x],z = pre[y]; int kd = (chd[z][0] == y); if (chd[y][kd] == x) { Rotate(x,!kd); Rotate(x,kd); } else { Rotate(y,kd); Rotate(x,kd); } } } pushup(x); if (goal == 0) root = x; } inline void RotateTo(int k,int goal) { int x = root; pushdown(x); while (sz[chd[x][0]] != k) { if (k < sz[chd[x][0]]) { x = chd[x][0]; } else { k -= (sz[chd[x][0]] + 1); x = chd[x][1]; } pushdown(x); } Splay(x,goal); } inline void erase(int x) { int fa = pre[x]; int head = 0, tail = 0; for (que[tail++] = x; head < tail; ++head) { ss[top2++] = que[head]; if (chd[que[head]][0]) que[tail++] = chd[que[head]][0]; if (chd[que[head]][1]) que[tail++] = chd[que[head]][1]; } chd[fa][chd[fa][1] == x] = 0; pushup(fa); } /* 以上基本为固定模板 */ inline void pushup(int rt) { sz[rt] = sz[chd[rt][0]] + sz[chd[rt][1]] + 1; sum[rt] = add[rt] + val[rt] + sum[chd[rt][0]] + sum[chd[rt][1]]; } inline void pushdown(int rt) { if (add[rt]) { val[rt] += add[rt]; add[chd[rt][0]] += add[rt]; add[chd[rt][1]] += add[rt]; sum[chd[rt][0]] += (ll)sz[chd[rt][0]]*add[rt]; sum[chd[rt][1]] += (ll)sz[chd[rt][1]]*add[rt]; add[rt] = 0; } } inline void newNode(int &x,int c) { if (top2) x = ss[--top2]; else x = ++top1; chd[x][0] = chd[x][1] = pre[x] = 0; sz[x] = 1; val[x] = sum[x] = c; add[x] = 0; } inline void makeTree(int &x,int l,int r, int f) { if (l > r) return ; int m = (l + r)>>1; newNode(x,num[m]); makeTree(chd[x][0],l,m - 1,x); makeTree(chd[x][1],m + 1,r,x); pre[x] = f; pushup(x); } inline void init(int n) { chd[0][0] = chd[0][1] = pre[0] = 0; add[0] = sum[0] = sz[0] = 0; root = top1 = 0; newNode(root,-1); // pre[root] = 0; newNode(chd[root][1],-1); pre[top1] = root; sz[root] = 2; for (int i = 0; i < n; ++i) scanf("%d",&num[i]); makeTree(keyTree,0,n - 1,chd[root][1]); pushup(chd[root][1]); pushup(root); } inline void update() { int l,r,c; scanf("%d%d%d",&l,&r,&c); RotateTo(l - 1,0); RotateTo(r + 1,root); add[keyTree] += c; sum[keyTree] += (ll)c*sz[keyTree]; } inline void query() { int l,r; scanf("%d%d",&l,&r); RotateTo(l - 1,0); RotateTo(r + 1,root); printf("%I64d ",sum[keyTree]); } ll sum[N]; int add[N]; int val[N]; int num[N]; }spt; int main() { // Read(); int n,m; scanf("%d%d",&n,&m); spt.init(n); while (m--) { char op[2]; scanf("%s",op); if (op[0] == 'Q') spt.query(); else spt.update(); } return 0; }
题意:
给你n个数,然后每次找到第i大的数,放到第i个位置,然后将第i大的数的位置 - 1到i这个区间逆置,求第i大的数的位置
思路:
直接Splay模拟,然后构造两个边界点,旋转达到求逆的效果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d ", x) #define lowbit(x) (x)&(-x) #define Read() freopen("data.in", "r", stdin) #define Write() freopen("d.out", "w", stdout) #define ll unsigned long long #define keyTree (chd[chd[root][1]][0]) #define M 100007 #define N 100017 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; int n; struct node { int val; int idx; }nd[N]; int cmp(node a,node b) { return a.val < b.val; } class SplayTree { public: int son[N][2],pre[N]; int rt,top; int sz[N],rev[N]; void Link(int x,int y,int c) { pre[x] = y; son[y][c] = x; } void Rotate(int x,int c) { int y = pre[x]; pushdown(y); pushdown(x); Link(x , pre[y], son[pre[y]][1] == y); Link(son[x][!c],y,c); Link(y,x,!c); pushup(y); } void Splay(int x,int g) { for (pushdown(x); pre[x] != g;) { int y = pre[x],cx = son[y][1] == x,cy = son[pre[y]][1] == y; if (pre[y] == g) Rotate(x,cx); else { if (cx == cy) Rotate(y,cy); else Rotate(x,cx); Rotate(x,cy); } } pushup(x); if (g == 0) rt = x; } int RotateTo(int k,int g) { int x = rt; pushdown(x); while (sz[son[x][0]] != k) { if (sz[son[x][0]] > k) x = son[x][0]; else k -= sz[son[x][0]] + 1, x = son[x][1]; pushdown(x); } Splay(x,g); return x; } void NewNode(int y ,int &x,int k) { x = ++top; pre[x] = y; sz[x] = 1; nd[k].idx = x; rev[x] = son[x][0] = son[x][1] = 0; } void pushup(int x) { sz[x] = sz[son[x][0]] + sz[son[x][1]] + 1; } void Reverse(int x) { rev[x] ^= 1; swap(son[x][0],son[x][1]); } void pushdown(int x) { if (rev[x]) { Reverse(son[x][0]); Reverse(son[x][1]); rev[x] = 0; } } void makeTree(int l,int r,int &x,int y) { if (l > r) return; int m = (l + r)>>1; NewNode(y,x,m); makeTree(l,m - 1,son[x][0],x); makeTree(m + 1,r,son[x][1],x); pushup(x); } void init() { for (int i = 1; i <= n; ++i) { scanf("%d",&nd[i].val); } NewNode(top = 0,rt,0); NewNode(rt,son[rt][1],0); makeTree(1,n,son[2][0],2); Splay(3,0); } void solve() { for (int i = 1; i <= n; ++i) { int idx = nd[i].idx; Splay(idx, 0); int ans = sz[son[rt][0]]; RotateTo(i - 1,0); int y = RotateTo(ans + 1,rt); Reverse(son[y][0]); printf("%d%c",ans,i < n ? ' ': ' '); } } }spt; int main() { // Read(); while (~scanf("%d",&n)) { if (!n) break; spt.init(); stable_sort(nd + 1,nd + 1 + n, cmp); // for (int i = 1; i <= n; ++i) printf("%d %d ",nd[i].val,nd[i].idx); spt.solve(); } return 0; }
题意:
给你一个序列1...n 有三种操作:
1. Top x :Take person x to the front of the queue
2. Query x: calculate the current position of person x
3. Rank x: calculate the current person at position x
输出每次的询问
思路:
首先我们关键是点的离散化,然后建立树上的边到数组的点的映射,然后利用Splay模拟这个过程就行。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d ", x) #define lowbit(x) (x)&(-x) #define Read() freopen("data.in", "r", stdin) #define Write() freopen("d.out", "w", stdout) #define ll unsigned long long #define keyTree (chd[chd[root][1]][0]) #define M 100007 #define N 300017 using namespace std; const int inf = 0x7f7f7f7f; const int mod = 1000000007; int p[N],a[N]; char op[N][6]; int s[N],e[N]; int tot; int n,m; class SplayTree { public: int sz[N]; int key[N],nd[N]; int son[N][2],pre[N]; int num[N]; int rt,top; inline void Link(int x,int y,int c) { pre[x] = y; son[y][c] = x; } inline void Rotate(int x,int c) { int y = pre[x]; Link(x,pre[y],son[pre[y]][1] == y); Link(son[x][!c],y,c); Link(y,x,!c); pushup(y); } inline void Splay(int x,int g) { for (; pre[x] != g;) { int y = pre[x], cx = son[y][1] == x, cy = son[pre[y]][1] == y; if (pre[y] == g) Rotate(x,cx); else { if (cx == cy) Rotate(y,cy); else Rotate(x,cx); Rotate(x,cy); } } pushup(x); if (g == 0) rt = x; } inline void pushup(int x) { sz[x] = sz[son[x][0]] + sz[son[x][1]] + num[x]; } inline void NewNode(int &x,int y,int c) { x = ++top; pre[x] = y; son[x][0] = son[x][1] = 0; sz[x] = num[x] = e[c] - s[c] + 1; key[x] = c; nd[c] = x;//将树上的点与数组的中的建立映射 } inline void makeTree(int l,int r,int &x,int f) { if (l > r) return ; int m = (l + r)>>1; NewNode(x,f,m); makeTree(l,m - 1,son[x][0],x); makeTree(m + 1,r,son[x][1],x); pushup(x); } inline void init() { son[0][0] = son[0][1] = pre[0] = 0; sz[0] = 0; num[0] = 0; top = 0; key[0] = nd[0] = 0; makeTree(0,tot - 1,rt,0); } inline void Query(int k) { int tx = lower_bound(s, s + tot, k) - s; int x = nd[tx]; Splay(x,0); printf("%d ",sz[son[rt][0]] + 1); } inline int Find(int k,int rt)//查找第k大 { int t = sz[son[rt][0]]; if (k <= t) return Find(k,son[rt][0]); else if (k <= t + num[rt]) return s[key[rt]] + (k - t) - 1; else return Find(k - (t + num[rt]),son[rt][1]); } inline int get_min(int x) { while (son[x][0]) x = son[x][0]; return x; } inline void Del_R() { if (!son[rt][0] || !son[rt][1]) { rt = son[rt][0] + son[rt][1]; pre[rt] = 0; } else { int k = get_min(son[rt][1]);//找到右子树中最小的点 Splay(k,rt);//旋转到根节点下边这样保证根节点达到右子树的左子树为空 //合并将根节点删除 son[son[rt][1]][0] = son[rt][0]; rt = son[rt][1]; pre[son[rt][0]] = rt; pre[rt] = 0; pushup(rt); } } //不断的往左插入 inline void Insert(int &x,int k,int f) { if (x == 0) { NewNode(x,f,k); return ; } Insert(son[x][0],k,x); pushup(x); } inline void Top(int k) { int tx = lower_bound(s, s + tot, k) - s; int x = nd[tx]; Splay(x,0); Del_R(); Insert(rt,tx,0); Splay(top,0);//这里如果不加会TLE致死。。 } inline void solve() { for (int i = 0; i < m; ++i) { if (op[i][0] == 'T') Top(a[i]); else if (op[i][0] == 'Q') Query(a[i]); else printf("%d ",Find(a[i],rt)); } } }spt; int main() { // Read(); int T,cas = 1; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); //对我们需要询问或者处理的点进行离散化,将不变的区间进行缩点离散 int k = 0; p[k++] = 1; for (int i = 0; i < m; ++i) { scanf("%s%d",op[i],&a[i]); if (op[i][0] == 'Q' || op[i][0] == 'T') { p[k++] = a[i]; } } p[k++] = n; sort(p,p + k); tot = 0; s[tot] = p[0]; e[tot++] = p[0]; for (int i = 1; i < k; ++i) { if (p[i] != p[i - 1]) { if (p[i - 1] + 1 < p[i])//将区间缩点 { s[tot] = p[i - 1] + 1; e[tot++] = p[i] - 1; } s[tot] = p[i]; e[tot++] = p[i]; } } printf("Case %d: ",cas++); spt.init(); spt.solve(); } return 0; }