题目大意

给定n个点以及每个点的权值,要你处理接下来的m个操作。操作有4种。操作从0到3编号。点从1到n编号。
0.询问从x到y的路径上的点的权值的xor和。保证x到y是联通的。
1.代表连接x到y,若x到y已经联通则无需连接。
2.后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在。
3.将点x上的权值变成y。

总体思路

在动态树中,结点是可以通过Link和Cut进行变换的,对于维护静态树的树链剖分可就不管用了。此时如何效率高地对多个点的值进行存储处理呢?如果可以利用上结点可以灵活地转动的splay树,这样就可以通过存储每个点的子树中的值的方式满足一个点对多个点的值进行存储了呀!

回顾Splay树,其实质上维护的是一个值递增的数列,每一棵子树维护的都是数列中连续的一段区间。那么在这个原图中,对于每一条链,上面的节点的深度根据父子关系都是递增的。如果我们把这条链上的节点按照深度排成一排,这也是一个数列,所以可以用Splay树来解决。

关于这棵splay树

splay树维护的是原图中的一条链,节点的key值为原图中节点的深度,每个节点维护其子树的Xor和。每个子树都维护一段子链。(注意,splay树中的排列方式与其在原图中具体谁与谁相邻无关)因此,splay树根节点的Xor值便是整条链的Xor和,它的父节点是连接splay树所维护的链的某个节点(具体为splay树最靠左的节点)的原图中的父节点。

既然这叫做动态树,所以树可以是不断变换的。如果我们要对两点间的路径进行查询,只要把一个点放在splay的树根上且没有右孩子(位于所在链链顶),另一个点放在splay树所维护的链的链底,这样就可以知道整条链上的值了。

基本操作

Access

Access(Node *cur)是生成结点cur到顶端Splay树根的一条链,需要用到链的断开与连接。具体操作就是从下往上找。对于一个splay树中的结点cur,将cur通过Splay使其成为当前splay树的树根,然后将当前这个树根cur的右孩子断开(此时cur就成为了链底),将其设为之前找到的Splay树的树根prev(一开始prev是个NULL,所以cur没有右孩子),并更新cur存储的Xor和,然后cur变为其父亲(也就是cur所在链的链顶的父亲),prev变为cur,由此不断循环,直到cur成为原图树根所在的Splay树的树根。

“经常用到的操作”Access后Splay

首先Access(cur),然后Splay(cur)。此时cur成为了顶端Splay树根。因为原来原图的树根在当前splay树的最左侧,cur在splay树的树根,还没有右孩子,所以说这个操作使得原图中的树根成为了链底,cur成为了链顶。

MakeRoot

MakeRoot(Node *cur)是将结点cur设为原图中的根节点。

“经常用到的操作”后,顶端Splay树上的节点里原根越近,离新根就越远。所以将顶端Splay树整体翻转即可。

关于翻转

翻转就是将自己的左右孩子交换,然后将自己的翻转标记取反。每次Splay之前都要PushDown,递归到当前Splay树的根节点,从根节点开始,如果当前结点有有Rev标记,则将当前结点的孩子的左右孩子交换。

MakePath

MakePath(Node *from, Node *to)是将from和to放到一个链内,并使to成为链顶,from成为链底。首先MakeRoot(from),然后“经常用到的操作”to即可。

FindRoot

FindRoot(Node *cur)指以cur为起点,找到原图的树根。“经常用到的操作”cur,然后不断找左孩子即可。

应用操作

Query

Query(Node *from, Node *to)是要查询from到to的路径的Xor和。MakePath(from, to)后,因为splay树根节点的Xor值便是整条链的Xor和,所以to的Xor值即为所求。

Link

Link(Node *from, Node *to)是要连接from和to。首先MakeRoot(from),然后如果FindRoot(to)!=from,则将from的Father设成to即可。(注意必须是MakeRoot(from),而不是“经常用到的操作”from,因为这样并没有给father设置,而是给链的根设置了)。

Cut

Cut(Node *from, Node *to)是要剪掉from和to。首先MakePath(from, to),如果from和to已经连接,则两点间的路径就是两点的边,又因为MakePath后to是链顶,所以如果to->LeftSon==from,再Cut。

Modify

Modify(Node *cur)是要修改cur的值。先把cur Splay到其所在Splay树的树根,然后改cur的值并Refresh。这样避免了对整个树进行更新。

#include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std; const int MAX_NODE = 300010;
#define LOOP(i, n) for(int i=1; i<=n; i++) struct LctTree
{
private:
struct Node
{
Node *Father, *LeftSon, *RightSon;
int Weight, Xor, Id;
bool ReverseFlag; Node()
{
Weight = Xor = 0;
LeftSon = RightSon = Father = NULL;
ReverseFlag = false;
} Node(int weight) :Weight(weight), Xor(weight)
{
LeftSon = RightSon = Father = NULL;
ReverseFlag = false;
} void Reverse()
{
swap(LeftSon, RightSon);
ReverseFlag = !ReverseFlag;
} void PushDown()
{
if (ReverseFlag)
{
if (LeftSon)//不要忘了判断
LeftSon->Reverse();
if (RightSon)//不要忘了判断
RightSon->Reverse();
ReverseFlag = false;
}
} bool IsRoot()
{
return Father == NULL || (Father->LeftSon != this && Father->RightSon != this);
} bool IsLeftSon()
{
return Father->LeftSon == this;
} void Refresh()
{
Xor = (LeftSon ? LeftSon->Xor : 0) ^ (RightSon ? RightSon->Xor : 0) ^ Weight;
}
}_nodes[MAX_NODE]; struct SplayTree
{
private:
Node *InnerRoot; void PushDown(Node *cur)
{
if (!cur->IsRoot())
PushDown(cur->Father);
cur->PushDown();
} void Rotate(Node *cur)
{
Node *gfa = cur->Father->Father;
Node **gfaSon = cur->Father->IsRoot() ? &InnerRoot : (cur->Father->IsLeftSon() ? &gfa->LeftSon : &gfa->RightSon);//TODO
Node **faSon = cur->IsLeftSon() ? &cur->Father->LeftSon : &cur->Father->RightSon;
Node **curSon = cur->IsLeftSon() ? &cur->RightSon : &cur->LeftSon;
*faSon = *curSon;
if (*faSon)
(*faSon)->Father = cur->Father;
*curSon = cur->Father;
(*curSon)->Father = cur;
*gfaSon = cur;
(*gfaSon)->Father = gfa;
(*curSon)->Refresh();
cur->Refresh();
} public:
void Splay(Node *cur)
{
PushDown(cur);
while (!cur->IsRoot())
{
if (!cur->Father->IsRoot())
Rotate(cur->Father->IsLeftSon() == cur->IsLeftSon() ? cur->Father : cur);
Rotate(cur);
}
}
}AuTree; void Access(Node *cur)
{
Node *prev = NULL;
while (cur)//是cur,不是cur->IsRoot()。
{
AuTree.Splay(cur);
cur->RightSon = prev;
cur->Refresh();
prev = cur;
cur = cur->Father;
}
} Node *FindRoot(Node *cur)
{
Access(cur);
AuTree.Splay(cur);
while (cur->LeftSon)
cur = cur->LeftSon;
return cur;
} void MakeRoot(Node *cur)
{
Access(cur);
AuTree.Splay(cur);
cur->Reverse();
} void MakePath(Node *from, Node *to)
{
MakeRoot(from);
Access(to);
AuTree.Splay(to);
} int Query(Node *from, Node *to)
{
MakePath(from, to);
return to->Xor;
} void Link(Node *from, Node *to)
{
MakeRoot(from);
if (FindRoot(to) != from)
from->Father = to;
} void Cut(Node *from, Node *to)
{
MakePath(from, to);
if (to->LeftSon == from)
from->Father = to->LeftSon = NULL;
} void Modify(Node *cur, int w)
{
Access(cur);
AuTree.Splay(cur);
cur->Weight = w;
cur->Refresh();
} public:
void SetNode(int id, int weight)
{
Node *cur = _nodes + id;
cur->Id = id;
cur->Weight = cur->Xor = weight;//易忘点:cur->Xor
} int Query(int u, int v)
{
return Query(_nodes + u, _nodes + v);
} void Link(int u, int v)
{
Link(_nodes + u, _nodes + v);
} void Cut(int u, int v)
{
Cut(_nodes + u, _nodes + v);//TODO//别写成Link了
} void Modify(int u, int w)
{
Modify(_nodes + u, w);
}
}g; int main()
{
int nodeCnt, opCnt, w, op, u, v;
scanf("%d%d", &nodeCnt, &opCnt);
LOOP(i, nodeCnt)
{
scanf("%d", &w);
g.SetNode(i, w);
}
while (opCnt--)
{
scanf("%d", &op);
switch (op)
{
case 0://Query
scanf("%d%d", &u, &v);
printf("%d\n", g.Query(u, v));
break;
case 1://Link
scanf("%d%d", &u, &v);
g.Link(u, v);
break;
case 2://Cut
scanf("%d%d", &u, &v);
g.Cut(u, v);
break;
case 3://Modify
scanf("%d%d", &u, &w);
g.Modify(u, w);
break;
}
}
return 0;
}

  

luogu3690 【模板】 Link Cut Tree(动态树)的更多相关文章

  1. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  2. 洛谷.3690.[模板]Link Cut Tree(动态树)

    题目链接 LCT(良心总结) #include <cstdio> #include <cctype> #include <algorithm> #define gc ...

  3. Link Cut Tree 动态树 小结

    动态树有些类似 树链剖分+并查集 的思想,是用splay维护的 lct的根是动态的,"轻重链"也是动态的,所以并没有真正的轻重链 动态树的操作核心是把你要把 修改/询问/... 等 ...

  4. LCT(link cut tree) 动态树

    模板参考:https://blog.csdn.net/saramanda/article/details/55253627 综合各位大大博客后整理的模板: #include<iostream&g ...

  5. 洛谷P3690 Link Cut Tree (动态树)

    干脆整个LCT模板吧. 缺个链上修改和子树操作,链上修改的话join(u,v)然后把v splay到树根再打个标记就好. 至于子树操作...以后有空的话再学(咕咕咕警告) #include<bi ...

  6. 洛谷P3690 [模板] Link Cut Tree [LCT]

    题目传送门 Link Cut Tree 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代 ...

  7. 模板Link Cut Tree (动态树)

    题目描述 给定N个点以及每个点的权值,要你处理接下来的M个操作.操作有4种.操作从0到3编号.点从1到N编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联 ...

  8. 【刷题】洛谷 P3690 【模板】Link Cut Tree (动态树)

    题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor ...

  9. P3690 【模板】Link Cut Tree (动态树)

    P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...

  10. LuoguP3690 【模板】Link Cut Tree (动态树) LCT模板

    P3690 [模板]Link Cut Tree (动态树) 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两 ...

随机推荐

  1. 跳出双重for循环的案例__________跳出当前循环(continue out)

    package com.etc.operator; public class demo { public static void main(String[] args) { // break out; ...

  2. winxp精简版没有IIS的解决办法

    首先在“开始”菜单的“运行”中输入“c:\Windows\inf\sysoc.inf”,系统会自动使用记事本打开sysoc.inf这个文件.在sysoc.inf中找到“[Components]”这一段 ...

  3. js-学习方法

    1:多实践,找例子,看别人是如何实现的,然后自己去实现,然后谷歌百度,最后总结. 2:如何读js英文书:不是自己不会读,是被吓着了.自己吓自己. 英文不好的话,先不要挨着排的从头到尾读. 应该首先读目 ...

  4. mui图片懒加载

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name ...

  5. hibernate_03_session详解

    获得session对象有两种方法: 1)openSession 2)getCurrentSession 如果使用的是getCurrentSession需要在hibernate.cfg.xml文件中进行 ...

  6. MVC5+EasyUI+EF6+Linq通用权限系统出炉--登录(2)

    1.输入验证码后 自动识别验证码并登录.

  7. BRAFT EDITOR富文本编辑器

    https://braft.margox.cn/demos/basic     官方文档 import React from 'react' import Uploading from '../Upl ...

  8. TCP协议的三次握手、四次挥手

    TCP三次握手 TCP的连接的建立需要发送三个包,一次称为三次握手(Three-way Handshake). 三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 ...

  9. SqlLite提高批量插入速度的方法及原因分析

    (1)-SQLite忽略大小写查询大部分数据库在进行字符串比较时,对大小写是不敏感的.但是SQLite却是大小写敏感的.如果想让SQLite忽略大小写,方法如下:方法一:使用大小写转换函数LOWER. ...

  10. BZOJ 2959: 长跑 LCT_并查集_点双

    真tm恶心...... Code: #include<bits/stdc++.h> #define maxn 1000000 using namespace std; void setIO ...