Splay树学习
首先给出一论文讲的很好:
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树,然后找他的左子树中最靠右的,右子树中最靠左的那肯定和他的距离差值是最小的。然后求和即可。
#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\n", 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 = ; struct SplayTree
{
int chd[N][],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][] = chd[r][] = ;
}
//旋转
void Rotate(int x,int kd)
{
int y = pre[x];
chd[y][!kd] = chd[x][kd];
pre[chd[x][kd]] = y; if (pre[y] != ) chd[pre[y]][chd[pre[y]][] == 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]][] == r);
else
{
int y = pre[r];
int kd = chd[pre[y]][] == y;
if (chd[pre[r]][kd] == r)
{
Rotate(r,!kd);
Rotate(r,kd);
}
else
{
Rotate(y,kd);
Rotate(r,kd);
}
}
}
if (g == ) root = r;
}
//插入
int insert(int k)
{
int r = root;
while (chd[r][key[r] < k])
{
if (key[r] == k)
{
Splay(r,);
return ;
}
r = chd[r][key[r] < k];
}
newNode(chd[r][key[r]< k],r,k);
Splay(chd[r][key[r] < k],);
return ;
}
int get_next(int x)
{
int rg = chd[x][];
if (rg == ) return inf;
while (chd[rg][]) rg = chd[rg][];
return key[rg] - key[x];
}
int get_pre(int x)
{
int lt = chd[x][];
if (lt == ) return inf;
while (chd[lt][]) lt = chd[lt][];
return key[x] - key[lt];
}
}spt; int n;
int main()
{
// Read();
int x;
while (~scanf("%d",&n))
{
spt.tot = spt.root = ;
int ans = ;
for (int i = ; i <= n; ++i)
{
scanf("%d",&x);
// cout<< ">>>>" << x << endl;
if (i == )
{
ans += x;
spt.newNode(spt.root,,x);
}
else
{
if (spt.insert(x) == ) continue;
int a = spt.get_next(spt.root);
int b = spt.get_pre(spt.root);
ans += min(a,b);
}
// cout << ans << endl;
}
printf("%d\n",ans);
}
return ;
}
线段树可解决的问题,Splay解决:
pku 3468 A Simple Problem with Integers
成段更新,区间询问
第二个论文里面有提到如何通过Splay的操作来实现。
#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\n", 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 = ; struct SpayTree
{
public:
int sz[N];
int ss[N],que[N];
int chd[N][],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]][] == 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]][] == x);
}
else
{
int y = pre[x],z = pre[y];
int kd = (chd[z][] == y);
if (chd[y][kd] == x)
{
Rotate(x,!kd); Rotate(x,kd);
}
else
{
Rotate(y,kd); Rotate(x,kd);
}
}
}
pushup(x);
if (goal == ) root = x;
}
inline void RotateTo(int k,int goal)
{
int x = root;
pushdown(x);
while (sz[chd[x][]] != k)
{
if (k < sz[chd[x][]])
{
x = chd[x][];
}
else
{
k -= (sz[chd[x][]] + );
x = chd[x][];
}
pushdown(x);
}
Splay(x,goal);
}
inline void erase(int x)
{
int fa = pre[x];
int head = , tail = ;
for (que[tail++] = x; head < tail; ++head)
{
ss[top2++] = que[head];
if (chd[que[head]][]) que[tail++] = chd[que[head]][];
if (chd[que[head]][]) que[tail++] = chd[que[head]][];
}
chd[fa][chd[fa][] == x] = ;
pushup(fa);
} /* 以上基本为固定模板 */ inline void pushup(int rt)
{
sz[rt] = sz[chd[rt][]] + sz[chd[rt][]] + ;
sum[rt] = add[rt] + val[rt] + sum[chd[rt][]] + sum[chd[rt][]];
}
inline void pushdown(int rt)
{
if (add[rt])
{
val[rt] += add[rt];
add[chd[rt][]] += add[rt];
add[chd[rt][]] += add[rt];
sum[chd[rt][]] += (ll)sz[chd[rt][]]*add[rt];
sum[chd[rt][]] += (ll)sz[chd[rt][]]*add[rt];
add[rt] = ;
}
}
inline void newNode(int &x,int c)
{
if (top2) x = ss[--top2];
else x = ++top1;
chd[x][] = chd[x][] = pre[x] = ;
sz[x] = ;
val[x] = sum[x] = c;
add[x] = ;
}
inline void makeTree(int &x,int l,int r, int f)
{
if (l > r) return ;
int m = (l + r)>>;
newNode(x,num[m]);
makeTree(chd[x][],l,m - ,x);
makeTree(chd[x][],m + ,r,x);
pre[x] = f;
pushup(x);
}
inline void init(int n)
{
chd[][] = chd[][] = pre[] = ;
add[] = sum[] = sz[] = ; root = top1 = ;
newNode(root,-);
// pre[root] = 0;
newNode(chd[root][],-);
pre[top1] = root;
sz[root] = ; for (int i = ; i < n; ++i) scanf("%d",&num[i]);
makeTree(keyTree,,n - ,chd[root][]);
pushup(chd[root][]); pushup(root);
} inline void update()
{
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
RotateTo(l - ,);
RotateTo(r + ,root);
add[keyTree] += c;
sum[keyTree] += (ll)c*sz[keyTree];
}
inline void query()
{
int l,r;
scanf("%d%d",&l,&r);
RotateTo(l - ,);
RotateTo(r + ,root);
printf("%I64d\n",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[];
scanf("%s",op);
if (op[] == 'Q') spt.query();
else spt.update();
}
return ;
}
题意:
给你n个数,然后每次找到第i大的数,放到第i个位置,然后将第i大的数的位置 - 1到i这个区间逆置,求第i大的数的位置
思路:
直接Splay模拟,然后构造两个边界点,旋转达到求逆的效果。
#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\n", 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 = ; 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][],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]][] == 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][] == x,cy = son[pre[y]][] == 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 == ) rt = x;
}
int RotateTo(int k,int g)
{
int x = rt;
pushdown(x);
while (sz[son[x][]] != k)
{
if (sz[son[x][]] > k) x = son[x][];
else k -= sz[son[x][]] + , x = son[x][];
pushdown(x);
}
Splay(x,g);
return x;
}
void NewNode(int y ,int &x,int k)
{
x = ++top;
pre[x] = y; sz[x] = ; nd[k].idx = x;
rev[x] = son[x][] = son[x][] = ;
}
void pushup(int x)
{
sz[x] = sz[son[x][]] + sz[son[x][]] + ;
}
void Reverse(int x)
{
rev[x] ^= ;
swap(son[x][],son[x][]);
}
void pushdown(int x)
{
if (rev[x])
{
Reverse(son[x][]);
Reverse(son[x][]);
rev[x] = ;
}
}
void makeTree(int l,int r,int &x,int y)
{
if (l > r) return;
int m = (l + r)>>;
NewNode(y,x,m);
makeTree(l,m - ,son[x][],x);
makeTree(m + ,r,son[x][],x);
pushup(x);
}
void init()
{
for (int i = ; i <= n; ++i)
{
scanf("%d",&nd[i].val);
}
NewNode(top = ,rt,);
NewNode(rt,son[rt][],);
makeTree(,n,son[][],);
Splay(,);
}
void solve()
{
for (int i = ; i <= n; ++i)
{
int idx = nd[i].idx;
Splay(idx, );
int ans = sz[son[rt][]];
RotateTo(i - ,);
int y = RotateTo(ans + ,rt);
Reverse(son[y][]);
printf("%d%c",ans,i < n ? ' ': '\n');
}
}
}spt; int main()
{
// Read();
while (~scanf("%d",&n))
{
if (!n) break;
spt.init();
stable_sort(nd + ,nd + + n, cmp);
// for (int i = 1; i <= n; ++i) printf("%d %d\n",nd[i].val,nd[i].idx);
spt.solve();
}
return ;
}
题意:
给你一个序列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模拟这个过程就行。
#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\n", 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 = ; int p[N],a[N];
char op[N][];
int s[N],e[N];
int tot;
int n,m; class SplayTree
{
public:
int sz[N];
int key[N],nd[N];
int son[N][],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]][] == 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][] == x, cy = son[pre[y]][] == 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 == ) rt = x;
}
inline void pushup(int x)
{
sz[x] = sz[son[x][]] + sz[son[x][]] + num[x];
}
inline void NewNode(int &x,int y,int c)
{
x = ++top; pre[x] = y;
son[x][] = son[x][] = ;
sz[x] = num[x] = e[c] - s[c] + ;
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)>>;
NewNode(x,f,m);
makeTree(l,m - ,son[x][],x);
makeTree(m + ,r,son[x][],x);
pushup(x);
}
inline void init()
{
son[][] = son[][] = pre[] = ;
sz[] = ; num[] = ; top = ;
key[] = nd[] = ;
makeTree(,tot - ,rt,);
}
inline void Query(int k)
{
int tx = lower_bound(s, s + tot, k) - s;
int x = nd[tx];
Splay(x,);
printf("%d\n",sz[son[rt][]] + );
}
inline int Find(int k,int rt)//查找第k大
{
int t = sz[son[rt][]];
if (k <= t) return Find(k,son[rt][]);
else if (k <= t + num[rt]) return s[key[rt]] + (k - t) - ;
else return Find(k - (t + num[rt]),son[rt][]);
}
inline int get_min(int x)
{
while (son[x][]) x = son[x][];
return x;
}
inline void Del_R()
{
if (!son[rt][] || !son[rt][])
{
rt = son[rt][] + son[rt][];
pre[rt] = ;
}
else
{
int k = get_min(son[rt][]);//找到右子树中最小的点
Splay(k,rt);//旋转到根节点下边这样保证根节点达到右子树的左子树为空
//合并将根节点删除
son[son[rt][]][] = son[rt][];
rt = son[rt][];
pre[son[rt][]] = rt;
pre[rt] = ;
pushup(rt);
}
}
//不断的往左插入
inline void Insert(int &x,int k,int f)
{
if (x == )
{
NewNode(x,f,k);
return ;
}
Insert(son[x][],k,x);
pushup(x);
}
inline void Top(int k)
{
int tx = lower_bound(s, s + tot, k) - s;
int x = nd[tx];
Splay(x,);
Del_R();
Insert(rt,tx,);
Splay(top,);//这里如果不加会TLE致死。。
}
inline void solve()
{
for (int i = ; i < m; ++i)
{
if (op[i][] == 'T') Top(a[i]);
else if (op[i][] == 'Q') Query(a[i]);
else printf("%d\n",Find(a[i],rt));
}
}
}spt; int main()
{
// Read();
int T,cas = ;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
//对我们需要询问或者处理的点进行离散化,将不变的区间进行缩点离散
int k = ;
p[k++] = ;
for (int i = ; i < m; ++i)
{
scanf("%s%d",op[i],&a[i]);
if (op[i][] == 'Q' || op[i][] == 'T')
{
p[k++] = a[i];
}
}
p[k++] = n; sort(p,p + k);
tot = ;
s[tot] = p[];
e[tot++] = p[]; for (int i = ; i < k; ++i)
{
if (p[i] != p[i - ])
{
if (p[i - ] + < p[i])//将区间缩点
{
s[tot] = p[i - ] + ;
e[tot++] = p[i] - ;
}
s[tot] = p[i];
e[tot++] = p[i];
}
}
printf("Case %d:\n",cas++);
spt.init();
spt.solve();
}
return ;
}
Splay树学习的更多相关文章
- 文艺平衡Splay树学习笔记(2)
本blog会讲一些简单的Splay的应用,包括但不局限于 1. Splay 维护数组下标,支持区间reserve操作,解决区间问题 2. Splay 的启发式合并(按元素多少合并) 3. 线段树+Sp ...
- 暑假学习日记:Splay树
从昨天开始我就想学这个伸展树了,今天花了一个上午2个多小时加下午2个多小时,学习了一下伸展树(Splay树),学习的时候主要是看别人博客啦~发现下面这个博客挺不错的http://zakir.is-pr ...
- Splay树再学习
队友最近可能在学Splay,然后让我敲下HDU1754的题,其实是很裸的一个线段树,不过用下Splay也无妨,他说他双旋超时,单旋过了,所以我就敲来看下.但是之前写的那个Splay越发的觉得不能看,所 ...
- BST,Splay平衡树学习笔记
BST,Splay平衡树学习笔记 1.二叉查找树BST BST是一种二叉树形结构,其特点就在于:每一个非叶子结点的值都大于他的左子树中的任意一个值,并都小于他的右子树中的任意一个值. 2.BST的用处 ...
- Splay树-Codevs 1296 营业额统计
Codevs 1296 营业额统计 题目描述 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司 ...
- ZOJ3765 Lights Splay树
非常裸的一棵Splay树,需要询问的是区间gcd,但是区间上每个数分成了两种状态,做的时候分别存在val[2]的数组里就好.区间gcd的时候基本上不支持区间的操作了吧..不然你一个区间里加一个数gcd ...
- 1439. Battle with You-Know-Who(splay树)
1439 路漫漫其修远兮~ 手抄一枚splay树 长长的模版.. 关于spaly树的讲解 网上很多随手贴一篇 貌似这题可以用什么bst啦 堆啦 平衡树啦 等等 这些本质都是有共同点的 查找.删除特 ...
- 伸展树(Splay树)的简要操作
伸展树(splay树),是二叉排序树的一种.[两个月之前写过,今天突然想写个博客...] 伸展树和一般的二叉排序树不同的是,在每次执行完插入.查询.删除等操作后,都会自动平衡这棵树.(说是自动,也就是 ...
- [Splay伸展树]splay树入门级教程
首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...
随机推荐
- 证书:数字签名和验签&加密和解密
用的是湖北省数字证书认证管理中心的签名和加密 1.带私钥的证书,即p12格式证书(后缀为.pfx) 2.不带私钥的证书,有多种格式,通常我们使用的是cer格式证书(后缀为.cer) 一. 1.什么是对 ...
- 分布式数据库主键id生成策略
分布式数据库部署主要分为两种,一种是读写分离.这个需要弄主从数据库.主要是写的时候写主数据库,读的时候读从数据库.分散读取压力,对于读多写少的系统有利于 提高其性能.还有一种是分布式存储,这种主要是将 ...
- 使用 Capistrano 进行自动化部署
最近在折腾这个,弄了好多次都不成功,看了官方文档和很多博客,都没有说清楚,因此,我觉得有必要把它记录下来,以帮助更多像我这样被弄得烦躁的人. 首先是安装,其实 Ubuntu 上面安装 Capistra ...
- R中绘制聚类的离散图
R中利用cluster简单的绘制常见聚类离散图 # 引入cluster库(clara.fanny) library(cluster) # 聚类散点图绘制 # 引入factoextra,cluster库 ...
- [SQL] 让特定的数据 排在最前
MYSQL目前常用的两种方法,如下: 让值为"张三" 的数据排在最前. -- 方法一 end asc -- 方法二 select * from tableName where co ...
- Windows上的巧克力味Chocolatey详解
Chocolatey是什么?很简单,Chocolatey就是Windows系统的yum或apt-get. 一.Chocolatey介绍 Chocolatey是一款专为Windows系统开发的.基于Nu ...
- (转)Terraform,自动化配置与编排必备利器
本文来自作者 QingCloud实践课堂 在 GitChat 上分享 「Terraform,自动化配置与编排必备利器」 Terraform - Infrastructure as Code 什么是 T ...
- soapUI-DataSource
1.1.1.1 概述 - 数据源 Option Description Properties DataSource属性表 Toolbar DataSource工具栏 Configura ...
- Java基础反射(二)
原文地址http://blog.csdn.net/sinat_38259539/article/details/71799078 反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Cla ...
- 1.2 Getting Started--Naming Conventions(命名约定)
Ember.js使用一个运行时解析器去连接你的对象而没有很多样板文件.作为一个开发者,如果你把code放到约定好的位置这个解析器会自动工作. 一.The Application 1. 当你 ...