LCT维护子树信息
有些题目,在要求支持link-cut之外,还会在线询问某个子树的信息。LCT可以通过维护虚边信息完成这个操作。
对于LCT上每个节点,维护两个两sz和si,后者维护该点所有虚儿子的信息,前者维护该点的所有信息和。
那么显然有:$si[x]=\sum sz[pson]$,$sz[x]=sz[lson]+sz[rson]+si[x]+v[x]$。
其中pson是虚儿子,lson,rson是LCT上的实儿子,v是节点本身的信息。
那么,考虑在哪些操作下需要更新这两个值。
1.access 每次将旧虚儿子从si中减去,加入新虚儿子的贡献。
2.link f[x]=y后,将x的信息计入si[y]。
别的操作要注意pushup。
这样我们维护了sz和si这两个变量,当想查询y的子树的信息和的时候,只需access(y),splay(y)后查询si[y]即可。
[BZOJ4530][BJOI2014]大融合
n个点,n-1次加边,最后形成一棵树。加边过程中会询问某条边现在被多少对点(x,y)之间的路径经过。
显然是link的过程中查询子树大小,最后询问的就是(整个连通块的大小-x子树的大小)*(x子树的大小)。
按上面的方法维护子树大小即可。
#include<cstdio>
#include<algorithm>
#define lc ch[x][0]
#define rc ch[x][1]
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
char op;
int n,Q,u,v,sz[N],f[N],ch[N][],rev[N],si[N]; bool isrt(int x){ return (!f[x]) || (ch[f[x]][]!=x && ch[f[x]][]!=x); }
void upd(int x){ sz[x]=sz[lc]+sz[rc]+si[x]+; }
void put(int x){ swap(lc,rc); rev[x]^=; }
void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=; }
void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); } void rot(int x){
int y=f[x],z=f[y],w=ch[y][]==x;
if (!isrt(y)) ch[z][ch[z][]==y]=x;
f[x]=z; f[y]=x; f[ch[x][w^]]=y;
ch[y][w]=ch[x][w^]; ch[x][w^]=y; upd(y);
} void splay(int x){
pd(x);
while (!isrt(x)){
int y=f[x],z=f[y];
if (!isrt(y)) (ch[y][]==x)^(ch[z][]==y) ? rot(x) : rot(y);
rot(x);
}
upd(x);
} void access(int x){
for (int y=; x; y=x,x=f[x])
splay(x),si[x]=si[x]+sz[rc]-sz[y],rc=y,upd(x);
} void mkrt(int x){ access(x); splay(x); put(x); } void link(int x,int y){
mkrt(x); access(y); splay(y);
f[x]=y; si[y]+=sz[x]; upd(y);
} ll que(int x,int y){ mkrt(x); access(y); splay(y); return 1ll*(sz[y]-sz[x])*sz[x]; } int main(){
scanf("%d%d",&n,&Q);
rep(i,,n) sz[i]=;
while (Q--){
scanf(" %c%d%d",&op,&u,&v);
if (op=='A') link(u,v); else printf("%lld\n",que(u,v));
}
return ;
}
[BZOJ3510]首都
link的同时询问某个连通块的重心,以及所有连通块重心编号的异或和。
仍然考虑用LCT维护子树大小。每次link两个连通块时,先作普通的link操作,然后求出新的连通块的重心。
显然新重心一定在原来两个连通块重心的连线上,split出这条链后在链上二分。
用ls,rs分别维护这条链两端已被二分排除的部分的大小,每次如果发现当前点合法则更新答案,否则往大的子树那边跳。
记得pushdown。多splay以保证复杂度。
#include<cstdio>
#include<algorithm>
#define lc ch[x][0]
#define rc ch[x][1]
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
char op[];
int n,Q,ans,x,y,sz[N],fa[N],f[N],ch[N][],rev[N],si[N]; int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); }
bool isrt(int x){ return (!f[x]) || (ch[f[x]][]!=x && ch[f[x]][]!=x); }
void upd(int x){ sz[x]=sz[lc]+sz[rc]+si[x]+; }
void put(int x){ swap(lc,rc); rev[x]^=; }
void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=; }
void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); } void rot(int x){
int y=f[x],z=f[y],w=ch[y][]==x;
if (!isrt(y)) ch[z][ch[z][]==y]=x;
f[x]=z; f[y]=x; f[ch[x][w^]]=y;
ch[y][w]=ch[x][w^]; ch[x][w^]=y; upd(y);
} void splay(int x){
pd(x);
while (!isrt(x)){
int y=f[x],z=f[y];
if (!isrt(y)) (ch[y][]==x)^(ch[z][]==y) ? rot(x) : rot(y);
rot(x);
}
upd(x);
} void access(int x){
for (int y=; x; y=x,x=f[x])
splay(x),si[x]=si[x]+sz[rc]-sz[y],rc=y,upd(x);
} void mkrt(int x){ access(x); splay(x); put(x); } void split(int x,int y){ mkrt(x); access(y); splay(y); } void link(int x,int y){
mkrt(x); mkrt(y); f[x]=y; si[y]+=sz[x]; upd(y);
x=get(x); y=get(y); split(x,y);
int s=n+,now=y,ls=,rs=,lim=sz[y]/;
while (now){
push(now); int s1=ls+sz[ch[now][]],s2=rs+sz[ch[now][]];
if (s1<=lim && s2<=lim) s=min(s,now);
if (s1>s2) rs+=sz[ch[now][]]+si[now]+,now=ch[now][];
else ls+=sz[ch[now][]]+si[now]+,now=ch[now][];
}
splay(s); fa[x]=fa[y]=fa[s]=s; ans=ans^x^y^s;
} int main(){
scanf("%d%d",&n,&Q);
rep(i,,n) ans^=i,sz[i]=,fa[i]=i;
while (Q--){
scanf("%s",op);
if (op[]=='X') printf("%d\n",ans);
else if (op[]=='Q') scanf("%d",&x),printf("%d\n",get(x));
else scanf("%d%d",&x,&y),link(x,y);
}
return ;
}
[UOJ#207]共价大爷游长沙
支持:往询问集合中添加删除路径,link-cut,询问某条边是否被集合中的所有路径经过。
一个显然的结论是,一条边(x,y)被所有路径经过,当且仅当整棵树以x为根时,所有路径恰有一个端点在y子树内。
于是我们将所有路径的两端都加上一个随机权值(不同路径用不同权值,同一路径两端用同一个权值),再询问y子树内所有点的权值异或和。
若等于所有路径随机权值的异或和则说明所有路径均恰有一端点在子树内。
随机种子要带时间戳否则会被Hack数据卡掉。
#include<cstdio>
#include<ctime>
#include<algorithm>
#define lc ch[x][0]
#define rc ch[x][1]
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int op;
int n,Q,ans,x,y,tot,sz[N],fa[N],f[N],ch[N][],rev[N],si[N],w[N*],tx[N*],ty[N*]; int Rand(){ return (rand()<<)+rand(); }
int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); }
bool isrt(int x){ return (!f[x]) || (ch[f[x]][]!=x && ch[f[x]][]!=x); }
void upd(int x){ sz[x]=sz[lc]^sz[rc]^si[x]; }
void put(int x){ swap(lc,rc); rev[x]^=; }
void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=; }
void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); } void rot(int x){
int y=f[x],z=f[y],w=ch[y][]==x;
if (!isrt(y)) ch[z][ch[z][]==y]=x;
f[x]=z; f[y]=x; f[ch[x][w^]]=y;
ch[y][w]=ch[x][w^]; ch[x][w^]=y; upd(y);
} void splay(int x){
pd(x);
while (!isrt(x)){
int y=f[x],z=f[y];
if (!isrt(y)) (ch[y][]==x)^(ch[z][]==y) ? rot(x) : rot(y);
rot(x);
}
upd(x);
} void access(int x){
for (int y=; x; y=x,x=f[x])
splay(x),si[x]=si[x]^sz[rc]^sz[y],rc=y,upd(x);
} void mkrt(int x){ access(x); splay(x); put(x); }
void split(int x,int y){ mkrt(x); access(y); splay(y); }
void link(int x,int y){ mkrt(x); access(y); splay(y); f[x]=y; si[y]^=sz[x]; }
void cut(int x,int y){ split(x,y); f[x]=ch[y][]=; upd(y); }
void add(int x,int k){ mkrt(x); sz[x]^=k; si[x]^=k; } int main(){
srand(time(NULL));
scanf("%*d%d%d",&n,&Q);
rep(i,,n) scanf("%d%d",&x,&y),link(x,y);
while (Q--){
scanf("%d",&op);
if (op==) scanf("%d%d",&x,&y),cut(x,y),scanf("%d%d",&x,&y),link(x,y);
if (op==){
tot++; scanf("%d%d",&tx[tot],&ty[tot]); w[tot]=Rand();
ans^=w[tot]; add(tx[tot],w[tot]); add(ty[tot],w[tot]);
}
if (op==) scanf("%d",&x),ans^=w[x],add(tx[x],w[x]),add(ty[x],w[x]);
if (op==) scanf("%d%d",&x,&y),split(x,y),puts((si[y]==ans)?"YES":"NO");
}
return ;
}
LCT维护子树信息的更多相关文章
- 【bzoj4530】[Bjoi2014]大融合 LCT维护子树信息
题目描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量 ...
- 【uoj#207】共价大爷游长沙 随机化+LCT维护子树信息
题目描述 给出一棵树和一个点对集合S,多次改变这棵树的形态.在集合中加入或删除点对,或询问集合内的每组点对之间的路径是否都经过某条给定边. 输入 输入的第一行包含一个整数 id,表示测试数据编号,如第 ...
- 【bzoj3510】首都 LCT维护子树信息(+启发式合并)
题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...
- 【BZOJ3510】首都 LCT维护子树信息+启发式合并
[BZOJ3510]首都 Description 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打 ...
- 共价大爷游长沙 lct 维护子树信息
这个题目的关键就是判断 大爷所有可能会走的路 会不会经过询问的边. 某一条路径经过其中的一条边, 那么2个端点是在这条边的2测的. 现在我们要判断所有的路径是不是都经过 u -> v 我们以u为 ...
- bzoj3510 首都 LCT 维护子树信息+树的重心
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3510 题解 首先每一个连通块的首都根据定义,显然就是直径. 然后考虑直径的几个性质: 定义:删 ...
- $LCT$维护子树信息学习笔记
\(LCT\)维护子树信息学习笔记 昨天\(FDF\)好题分享投了 \([ZJOI2018]\)历史 这题. 然后我顺势学学这个姿势. 结果调了一年...于是写个笔记记录一下. 基本原理 比较显然地, ...
- 【LCT维护子树信息】uoj207 共价大爷游长沙
这道题思路方面就不多讲了,主要是通过这题学一下lct维护子树信息. lct某节点u的子树信息由其重链的一棵splay上信息和若干轻儿子子树信息合并而成. splay是有子树结构的,可以在rotate, ...
- 洛谷4219 BJOI2014大融合(LCT维护子树信息)
QWQ 这个题目是LCT维护子树信息的经典应用 根据题目信息来看,对于一个这条边的两个端点各自的\(size\)乘起来,不过这个应该算呢? 我们可以考虑在LCT上多维护一个\(xv[i]\)表示\(i ...
- BZOJ4530[Bjoi2014]大融合——LCT维护子树信息
题目描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数 ...
随机推荐
- C++ Primer 5th 第19章 特殊工具与技术
C++是一种通用型语言,其设计者希望它能处理各种各样的问题,因此除了一些能适用于所有问题的语言特性,还有一些适用于特定问题的特性. 控制内存分配 某些程序对内存分配有着特殊的需求,它们不适合使用标准的 ...
- [转]google gflags 库完全使用
简单介绍 gflags 是 google 开源的用于处理命令行参数的项目. 安装编译 项目主页:gflags ➜ ~ git clone https://github.com/gflags/gflag ...
- C++ Qt多线程 TcpSocket服务器实例
服务器: incomming incomming.pro #------------------------------------------------- # # Project created ...
- linux可运行的shell脚本与设置开机服务启动(自己总结)
完整的ln命令参考:http://www.runoob.com/linux/linux-comm-ln.html ln :创建连接文件 - 默认创建的是硬连接,好比复制 ,但是两个文件会同步 命令:l ...
- 【CTF MISC】文件内容反转方法-2017世安杯CTF writeup详解
Reverseme 用winhex打开,发现里面的字符反过来可以正常阅读,所以文件被倒置了 Python解题程序如下 with open('reverseMe','rb') as f: with op ...
- mysql状态查看 QPS/TPS/缓存命中率查看【转】
运行中的mysql状态查看 对正在运行的mysql进行监控,其中一个方式就是查看mysql运行状态. (1)QPS(每秒Query量) QPS = Questions(or Queries ...
- java浅复制与深使用接口实现
1.浅复制与深复制概念⑴浅复制(浅克隆)被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵深复 ...
- 『实践』百度地图给多个marker添加右键菜单(删除、更新)
js: $.getJSON("./GetStationPlaceServlet",function(json){ for(var i=0;i<json.length;i++) ...
- shell 数组基础->
数组其实也算是变量, 传统的变量只能存储一个值, 但数组可以存储多个值. 普通数组:只能使用整数 作为数组索引 [有序 0 1 2 3 4 ]关联数组:可以使用字符串 作为数组索引 [无序 name ...
- [shell]shell中if语句的使用
转自:http://lovelace.blog.51cto.com/1028430/1211353 bash中如何实现条件判断?条件测试类型: 整数测试 字符测试 文件测试 一.条件 ...