Splay_Tree 模板(区间修改,旋转操作)
1.旋转操作
#define MAXN 100100 bool Add[MAXN];//延迟标记 struct Splay_Tree
{
int cnt, rt;//cnt为节点数,rt == root struct Tree{
int key;//关键字
int num, size;//num是这个节点有多少重复,size是以这个节点为根的子树大小。
int fa, son[];
}T[MAXN]; inline void init()
{
cnt = ;//初始化超级根节点(标记为0的节点)
T[].size = ;
rt = ;
memset(Add,,sizeof(Add));//开始初始化0
}
inline void PushUp(int x)
{
T[x].size=T[T[x].son[]].size+T[T[x].son[]].size+T[x].num;
} inline void PushDown(int x)
{
//翻转操作,这一步最为关键
if(Add[x])
{
swap(T[x].son[],T[x].son[]);
Add[T[x].son[]] ^= ;
Add[T[x].son[]] ^= ;
Add[x] = ;
}
} inline int Newnode(int key, int fa) //新建一个节点并返回
{
++cnt;
T[cnt].key=key;
T[cnt].num=T[cnt].size=;
T[cnt].fa=fa;
T[cnt].son[]=T[cnt].son[]=;
return cnt;
} inline void Rotate(int x, int p) //0左旋 1右旋
{
int y=T[x].fa;
PushDown(y);//你是说这个有问题。
PushDown(x);
T[y].son[!p]=T[x].son[p];
T[T[x].son[p]].fa=y;
T[x].fa=T[y].fa;
if(T[x].fa)
T[T[x].fa].son[T[T[x].fa].son[] == y]=x;
T[x].son[p]=y;
T[y].fa=x;
PushUp(y);
PushUp(x);
} void Splay(int x, int To) //将x节点移动到To的子节点中
{
while(T[x].fa != To)
{ if(T[T[x].fa].fa == To)
{
//在这里面得
PushDown(T[x].fa);
PushDown(x);
Rotate(x, T[T[x].fa].son[] == x);
}
else
{
int y=T[x].fa, z=T[y].fa;
PushDown(z);
PushDown(y);
PushDown(x);
int p=(T[z].son[] == y);
if(T[y].son[p] == x)
Rotate(x, !p), Rotate(x, p); //之字旋
else
Rotate(y, p), Rotate(x, p); //一字旋
}
}
if(To == ) rt=x;
} int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
{
if(!rt || p > T[rt].size) return ;
int x=rt;
while(x)
{
PushDown(x);
if(p >= T[T[x].son[]].size+ && p <= T[T[x].son[]].size+T[x].num)
break;
if(p > T[T[x].son[]].size+T[x].num)
{
p-=T[T[x].son[]].size+T[x].num;
x=T[x].son[];
}
else
x=T[x].son[];
}
Splay(x, );
return x;
} int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{
if(!rt) return ;
int x=rt;
while(x)
{
PushDown(x);
if(T[x].key == key) break;
x=T[x].son[key > T[x].key];
}
if(x) Splay(x, );
return x;
} int Prev() //返回根节点的前驱 非重点
{
if(!rt || !T[rt].son[]) return ;
int x=T[rt].son[];
PushDown(x);
while(T[x].son[])
{
x=T[x].son[];
PushDown(x);
}
Splay(x, );
return x;
} int next() //返回根结点的后继 非重点
{
if(!rt || !T[rt].son[]) return ;
int x=T[rt].son[];
PushDown(x);
while(T[x].son[])
{
x=T[x].son[];
PushDown(x);
}
Splay(x, );
return x;
} void Insert(int key) //插入key值
{
if(!rt)
rt=Newnode(key, );
else
{
int x=rt, y=;
while(x)
{
PushDown(x);
y=x;
if(T[x].key == key)
{
T[x].num++;
T[x].size++;
break;
}
T[x].size++;//既然一定调整
x=T[x].son[key > T[x].key];
}
if(!x)
x = T[y].son[key > T[y].key] = Newnode(key, y);
Splay(x, );
}
} void Delete()//直接改成删除根节点
{
if(rt == ) return ;//已经没有节点了
if(T[rt].num > )
{
T[rt].num --;
T[rt].size --;
return ;
}
if(T[rt].son[] == )
{
rt = T[rt].son[];
T[rt].fa = ;
}
else
{
int preid = T[rt].son[];
PushDown(preid);
while(T[preid].son[])
{
preid = T[preid].son[];
PushDown(preid);
}
Splay(preid, rt);
T[preid].son[] = T[rt].son[];
T[ T[rt].son[] ].fa = preid;
rt = preid;
T[rt].fa = ;
PushUp(rt);
}
} // int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
// {
// //我没有写PUSH_UP 和 PUSH_DOWN
// if(!rt) return 0;
// int x=rt, ret=0, y=0;
// while(x)
// {
// y=x;
// if(T[x].key <= key)
// {
// ret += T[T[x].son[0]].size + T[x].num;
// x=T[x].son[1];
// }
// else
// x=T[x].son[0];
// }
// Splay(y, 0);
// return ret;
// } // 这个删除太丑了
// void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
// {
// if(!Find(l)) Insert(l);// 你这样写真的好吗? 泥煤
// int p=Prev();
// if(!Find(r)) Insert(r);
// int q=next();
// if(!p && !q)
// {
// rt=0;
// return;
// }
// if(!p)
// {
// T[rt].son[0]=0;
// PushUp(rt);
// return;
// }
// if(!q)
// {
// Splay(p, 0);
// T[rt].son[1]=0;
// PushUp(rt);
// return;
// }
// Splay(p, q);
// T[p].son[1]=0;
// PushUp(p);
// PushUp(q);
// } void display(int x)
{
if(x==) return ;
PushDown(x);
display(T[x].son[]);
printf("%d ",T[x].key);
display(T[x].son[]);
} }spt; //
//int main() {
// //SPT 独特的旋转操作!
// int n;
// while(scanf("%d",&n) && n)
// {
// spt.init();
// for(int i=1;i<=n;i++)
// {
// int tmp;
// scanf("%d",&tmp);
// g[i].key = tmp;
// g[i].id = i;
// spt.Insert(i);
// }
//
// sort(g+1,g+1+n,cmp);
// for(int i=1;i<=n;i++)//开始旋转
// {
// //step one 将当前最小的点,移动到树根处
// spt.Splay(g[i].id, 0);
//
// //step two 将整个左子树旋转
// int sonid = spt.T[g[i].id].son[0];
// printf("%d",spt.T[sonid].size+i);
// if(i!=n) printf(" ");
// if(sonid != 0)
// {
// Add[sonid] ^= 1;//修改区间
// }
// spt.Delete();
//
// //每次操作之后,都把结果打印一遍
// //spt.display(spt.rt);
// //printf("\n");
// }
// printf("\n");
// }
// return 0;
//}
2.成段更#define MAXN 200100
long long Add[MAXN];//延迟标记 struct Splay_Tree
{
int cnt, rt;//cnt为节点数,rt == root struct Tree{
long long K;
long long sumk;
int key;//关键字
int num, size;//num是这个节点有多少重复,size是以这个节点为根的子树大小。
int fa, son[];
}T[MAXN]; inline void init()
{
cnt = ;//初始化超级根节点(标记为0的节点)
T[].size = ;
T[].sumk = ;
T[].K = ;
rt = ;
memset(Add,,sizeof(Add));
}
inline void PushUp(int x)
{
T[x].size=T[T[x].son[]].size+T[T[x].son[]].size+T[x].num;
T[x].sumk=T[T[x].son[]].sumk+T[T[x].son[]].sumk+T[x].K;
} inline void PushDown(int x)
{
if(Add[x])
{
if(T[x].son[])//
{
T[T[x].son[]].K += Add[x];
T[T[x].son[]].sumk += T[T[x].son[]].size*Add[x];
Add[T[x].son[]]+=Add[x];
}
if(T[x].son[])
{
T[T[x].son[]].K+=Add[x];
T[T[x].son[]].sumk += T[T[x].son[]].size*Add[x];
Add[T[x].son[]]+=Add[x];
}
Add[x]=;//不管子节点有没有,这层一定往下推,没有子节点相当于标记无效。
}
} inline int Newnode(int key, int fa,int K) //新建一个节点并返回
{
++cnt;
T[cnt].K = T[cnt].sumk = K;
T[cnt].key=key;
T[cnt].num=T[cnt].size=;
T[cnt].fa=fa;
T[cnt].son[]=T[cnt].son[]=;
return cnt;
} inline void Rotate(int x, int p) //0左旋 1右旋
{
int y=T[x].fa;
PushDown(y);//这里是不是必须的,我感觉从小往上不需要往上推
PushDown(x);
T[y].son[!p]=T[x].son[p];
T[T[x].son[p]].fa=y;
T[x].fa=T[y].fa;
if(T[x].fa)
T[T[x].fa].son[T[T[x].fa].son[] == y]=x;
T[x].son[p]=y;
T[y].fa=x;
PushUp(y);
PushUp(x);
} void Splay(int x, int To) //将x节点移动到To的子节点中
{
while(T[x].fa != To)
{
if(T[T[x].fa].fa == To)
Rotate(x, T[T[x].fa].son[] == x);
else
{
int y=T[x].fa, z=T[y].fa;
int p=(T[z].son[] == y);
if(T[y].son[p] == x)
Rotate(x, !p), Rotate(x, p); //之字旋
else
Rotate(y, p), Rotate(x, p); //一字旋
}
}
if(To == ) rt=x;
} int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
{
if(!rt || p > T[rt].size) return ;
int x=rt;
while(x)
{
PushDown(x);
if(p >= T[T[x].son[]].size+ && p <= T[T[x].son[]].size+T[x].num)
break;
if(p > T[T[x].son[]].size+T[x].num)
{
p-=T[T[x].son[]].size+T[x].num;
x=T[x].son[];
}
else
x=T[x].son[];
}
Splay(x, );
return x;
} int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{
if(!rt) return ;
int x=rt;
while(x)
{
PushDown(x);
if(T[x].key == key) break;
x=T[x].son[key > T[x].key];
}
if(x) Splay(x, );
return x;
} int Prev() //返回根节点的前驱 非重点
{
if(!rt || !T[rt].son[]) return ;
int x=T[rt].son[];
while(T[x].son[])
{
PushDown(x);
x=T[x].son[];
}
Splay(x, );
return x;
} int next() //返回根结点的后继 非重点
{
if(!rt || !T[rt].son[]) return ;
int x=T[rt].son[];
while(T[x].son[])
{
PushDown(x);
x=T[x].son[];
}
Splay(x, );
return x;
} void Insert(int key,int K) //插入key值
{
if(!rt)
rt=Newnode(key, , K);
else
{
int x=rt, y=;
while(x)
{
PushDown(x);
y=x;
if(T[x].key == key)
{
T[x].num++;
T[x].size++;
break;
}
T[x].size++;//既然一定调整
x=T[x].son[key > T[x].key];
}
if(!x)
x = T[y].son[key > T[y].key] = Newnode(key, y, K);
Splay(x, );
}
}
// void Delete()//直接改成删除根节点
// {
// if(rt == 0) return ;//已经没有节点了
// if(T[rt].num > 1)
// {
// T[rt].num --;
// T[rt].size --;
// return ;
// }
// if(T[rt].son[0] == 0)
// {
// rt = T[rt].son[1];
// T[rt].fa = 0;
// }
// else
// {
// int preid = T[rt].son[0];
// PushDown(preid);
// while(T[preid].son[1])
// {
// preid = T[preid].son[1];
// PushDown(preid);
// }
// Splay(preid, rt);
// T[preid].son[1] = T[rt].son[1];
// T[ T[rt].son[1] ].fa = preid;
// rt = preid;
// T[rt].fa = 0;
// PushUp(rt);
// }
// }
void Delete(int key) //删除值为key的节点1个,这个操作可能有问题。。。
{
int x=Find(key);
if(!x) return;
if(T[x].num>)
{
T[x].num--;
PushUp(x);
return;
}
int y=T[x].son[];
while(T[y].son[])
y=T[y].son[];
int z=T[x].son[];
while(T[z].son[])
z=T[z].son[];
if(!y && !z)
{
rt=;
return;
}
if(!y)
{
Splay(z, );
T[z].son[]=;
PushUp(z);
return;
}
if(!z)
{
Splay(y, );
T[y].son[]=;
PushUp(y);
return;
}
Splay(y, );
Splay(z, y);
T[z].son[]=;
PushUp(z);
PushUp(y);
} int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
{
if(!rt) return ;
int x=rt, ret=, y=;
while(x)
{
y=x;
if(T[x].key <= key)
{
ret += T[T[x].son[]].size + T[x].num;
x=T[x].son[];
}
else
x=T[x].son[];
}
Splay(y, );
return ret;
} // 这个删除太丑了
// void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
// {
// if(!Find(l)) Insert(l);// 你这样写真的好吗? 泥煤
// int p=Prev();
// if(!Find(r)) Insert(r);
// int q=next();
// if(!p && !q)
// {
// rt=0;
// return;
// }
// if(!p)
// {
// T[rt].son[0]=0;
// PushUp(rt);
// return;
// }
// if(!q)
// {
// Splay(p, 0);
// T[rt].son[1]=0;
// PushUp(rt);
// return;
// }
// Splay(p, q);
// T[p].son[1]=0;
// PushUp(p);
// PushUp(q);
// }
}spt;
Splay_Tree 模板(区间修改,旋转操作)的更多相关文章
- POJ 3468 A Simple Problem with Integers (伸展树区间更新求和操作 , 模板)
伸展数最基本操作的模板,区间求和,区间更新.为了方便理解,特定附上一自己搞的搓图 这是样例中的数据输入后建成的树,其中的1,2是加入的边界顶点,数字代表节点编号,我们如果要对一段区间[l, r]进行操 ...
- hdu1698 Just a Hook 【区间修改】(模板题)
题目链接:https://vjudge.net/contest/182746#problem/E 题目大意: 一段线段由n条小线段组成,每次操作把一个区间的小线段变成金银铜之一(金的价值为3,银为2, ...
- P3368 【模板】树状数组 2 单点查询与区间修改
题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. ...
- hdu 1890 Robotic SortI(splay区间旋转操作)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1890 题解:splay又一高级的功能,区间旋转这个是用线段树这些实现不了的,这题可以学习splay的旋 ...
- 树状数组求区间和模板 区间可修改 参考题目:牛客小白月赛 I 区间
从前有个东西叫树状数组,它可以轻易实现一些简单的序列操作,比如单点修改,区间求和;区间修改,单点求值等. 但是我们经常需要更高级的操作,比如区间修改区间查询.这时候树状数组就不起作用了,只能选择写一个 ...
- 线段树:Segment Tree(单点修改/区间修改模板) C++
线段树是非常有效的数据结构,可以快速的维护单点修改,区域修改,查询最大值,最小值等功能. 同时,它也很重要.如果有一天比赛,你卡在了一道线段树模板题目上,这就真的尴尬了.不过,随着时代的进步,题目也越 ...
- 题解报告:Luogu P3368 【模板】树状数组 2(区间修改,单点查询)
题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. ...
- POJ - 3468 线段树单点查询,单点修改区间查询,区间修改模板(求和)
题意: 给定一个数字n,表示这段区间的总长度.然后输入n个数,然后输入q,然后输入a,b,表示查询a,b,区间和,或者输入c 再输入三个数字a,b,c,更改a,b区间为c 思路: 线段树首先就是递归建 ...
- splay tree旋转操作 hdu 1890
很神奇的旋转操作. 目前没看到其他数据结构能实现这个功能.平衡树不好处理区间操作,线段树很难旋转.splay tree搞这个就很简单了. 下面用的这个模板跑了700ms,好慢,估计是删除操作太费时了, ...
随机推荐
- 谋哥:玩App怎么赚钱(三)
谋哥每天坚持写文章,如今写作速度是越来越快了,当然这样也能节省点时间.只是坚持每天写,确实须要极大的耐力和毅力,由于偶然事件会影响你心情和灵感.只是我一直相信秦刚老师(微信/QQ1111884 )说的 ...
- 微信小程序 - bindcontroltap和control的关系(map)
听说最近要废弃control,用cover-image和cover-view替代它,层级问题(我们此等萌新们还在想图标怎么显示在地图上(-.-)) 粗略的来说,一个展示(control),一个触发(b ...
- 语言那点事,crt
C语言标准(不管是ANSI 还是ISO)包含2部分,一部分是语言本身的标准,另一部分是C标准函数库.C标准函数库规定了函数的原型和功能,但是并没限定这些函数要怎么实现.所谓满足标准C规定的C编译器,不 ...
- Bootstrap-初步学习
<!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8&quo ...
- 使用CAsyncSocket总结
最近想起CAsyncSocket这个类,记得很早以前用过,现在却想不起来怎么用了,翻了翻以前的代码又看了看msdn感觉这个类做简单的异步socket太简单了,几行代码就可以搞定,在此先做个总结. 不管 ...
- Log4Net 最最最基本的应用。作为个人记录
本文只记录了将日志按照日期记录到文件中的方法. 注:1.如果将该方法封装在类库中,在引用类库的项目中添加配置文件. 2.如果程序为控制台程序.winfrom程序,需将配置文件存放在/bin/debug ...
- linux 系统下MySQL5.7重置root密码(完整版,含ERROR 1820 (HY000)解决方案)
mysql的root密码忘记了怎么重置? 往下看: 1.保证你的mysql服务处于关闭状态.(用ps -ef | grep mysql 查看进程,有则kill -9 进程号) 2.在my.cnf所在目 ...
- SVN Client API的.net 接口 SharpSvn介紹 Checkout操作实例
Subversion是一個文件版本管理工具, 廣泛的被大家採用來作為源代碼版本管理. 已有的工具不管是其自帶的命令行工具還是Windows UI的tortoiseSVN等還是很方便實用的, 但是如果想 ...
- Xilinx_7_Series_GTX简介
Xilinx_7_Series_GTX简介 https://wenku.baidu.com/view/4639cde7bb68a98270fefab6.html
- Django中API分析
下面,我将仔细分析一次请求的旅程: web端发出一个请求报文,到获得服务器的响应报文结束. 1.打开浏览器,输入URL,进入API页面: http://127.0.0.1:8000/api/salt ...