Top cluster 树分块
写点基础的东西。随便写的,勿喷。
top cluster
一个 cluster 是一个联通子图,且至多有两个点与其他部分连接
这两个点被称为 boundary node 其余点被称为 internal node,两个 boundary node 间的路径被称为 cluster path
而我们的树分块就是将原树分为 \(\sqrt n\) 个 cluster
将 boundary node 看成点,cluster 看成边,则构造出原图的收缩树
性质:不同的 cluster 最多共用两个 boundary node 而边集不交
构造方法
选取任意节点作为根,并强制根作为一个 boundary node
从根开始往下 dfs 并存储未归类进 cluster 的边(实际上存的是点,代表的是其连向其父亲的边)
当某个点 \(u\) 结束 dfs 假若发生以下三种情况之一则将其标记为 boundary node
\(u\) 未根节点
\(u\) 有至少两个子树中存在 boundary
栈中剩余边数量大于 \(B\)
好,假若点 \(u\) 成为了 boundary node 接下考虑如何把其子树划进不同的 cluster
我们贪心的在栈中选择极长前缀直至以下情况发生
子树用完
新加入一个子树会使当前 cluster 中 boundary 数量超过 \(2\)
新加入一个子树会使当前 cluster 大小超过 \(B\)
dfs 全部结束我们即可获得一种合法方案
根据论文里的结论,这样的划分 cluster 大小不超过 \(B\) 且数量不超过 \(6 \times \frac{n}{B}\)
贴一个板子题 P2420 (查询两点异或和)的代码,已经 AC
#include<bits/stdc++.h>
//#define int long long
//#define lowbit(x) (x&-(x))
using namespace std;
const int maxn = 1e5+114;
const int B = 318;
int fa[maxn];//原树上的父亲
int sz[maxn];//簇的大小
int tot;
int up[maxn],down[maxn];//所在的簇的上下界点
int dep[maxn];//深度
int st[maxn],top;//存贮还未分配的边
vector<int> edge[maxn];
int st_top[maxn];
int wait[maxn];//待分配的边
int low[maxn];//最浅界点
int CL[maxn],CLtot;
int CL_fa[maxn],CL_near[maxn],CL_up[maxn],CL_down[maxn];//在收缩树上的父亲 距离最近的簇路径上节点 所属簇的上下界点 (若为界点,则其所属簇为其作为下界点时所属的簇)
vector<int> cluster;//储存所有界点
vector<int> Down[maxn];//整个簇中出了上界点的点存入下界点中
int Point[maxn];//点权
int w[maxn];//一个簇上的上界点到根的路径上点权异或和
void work(int x){
for(int r=x;r!=0;r=fa[r]) w[x]^=Point[x];
}
void add_CL(int u,int v){//新建一个簇
if(!v) v=CL[CLtot];
CL_fa[v]=u,CL_near[u]=u;
for(int r=v;r!=u;r=fa[r])
CL_near[r]=r,w[v]^=Point[r];
for(int i=1;i<=CLtot;i++){
int r=CL[i],j;
CL_up[r]=u,CL_down[r]=v,Down[v].push_back(r);
for (j=r;!CL_near[j];j=fa[j]);
CL_near[r]=CL_near[j];
}
CLtot=0;
}
map<int,int> val[maxn];
void build(int u,int father){
dep[u]=dep[father]+1;
Point[u]=val[u][father];
fa[u]=father;
st_top[u]=top;
for(auto it=edge[u].begin();it!=edge[u].end();it++)
if((*it)==father){
edge[u].erase(it);
break;
}
wait[u]=true;
int cnt=0;
for(int v:edge[u]){
st[++top]=v;
build(v,u);
wait[u]+=wait[v];
low[v]&&(low[u]=low[v],cnt++);
}
if(wait[u]>B||cnt>1||father==0){
wait[u]=0,low[u]=u,cluster.push_back(u);
for(int i=0,j=st_top[u]+1,Cnt=0,cur_down=0,v;i<=edge[u].size();i++){
// Cnt 簇的大小 cur_down 簇的下界点
v=(i==edge[u].size())?0:edge[u][i];
if(Cnt+wait[v]>B||(cur_down&&low[v])||!v){ //已无法往当前簇中再加入一个子树
for (;(j<st_top[v]||!v)&&j<=top;j++)
CL[++CLtot]=st[j];
add_CL(u,cur_down),Cnt=cur_down=0;
}
Cnt+=wait[v],low[v]&&(cur_down=low[v]);
}
}
}
int n,q,rt;
int ask(int u){
int res=0;
while(u!=CL_up[u]) res^=Point[u],u=fa[u];
res^=w[u];
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
val[u][v]=val[v][u]=w;
edge[u].push_back(v);
edge[v].push_back(u);
}
build(1,0);
for(int x:cluster) work(x);
cin>>q;
for(int i=1;i<=q;i++){
int u,v;
cin>>u>>v;
cout<<(ask(u)^ask(v))<<'\n';
}
return 0;
}
接下来讲点运用:
路径分解:
我们将点 \(u \to rt\) 的路径分为三段。
\(u \to v\) 其中 \(v\) 是 \(u\) 往上走遇到的第一个在 cluster path 上的节点
\(v \to w\) 其中 \(w\) 是 \(v\) 往上走遇到的第一个 up boundary node
\(w \to rt\) 其中 \(rt\) 是根节点
根据 top cluster 的性质,第一条和第二条路径长度不大于 \(B\)
而第三条路径可以在收缩树上解决,收缩树的大小为 \(O(\frac{n}{B})\)
而在这些地方维护前缀和或者差分就可以做到查询修改一者 \(O(\sqrt n)\) 另一者 \(O(1)\)。
Top cluster 树分块的更多相关文章
- 【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)
2016-05-09 UPD:学习了新的DFS序列分块,然后发现这个东西是战术核导弹?反正比下面的树分块不知道要快到哪里去了 #include<cmath> #include<cst ...
- [BZOJ 1086] [SCOI2005] 王室联邦 【树分块】
题目链接:BZOJ - 1086 题目分析 这道题要求给树分块,使得每一块的大小在 [B, 3B] 之间,并且可以通过一个块外的节点(块根)使得整个块联通. 那么我们使用一种 DFS,维护一个栈,DF ...
- hdu 4366 Successor - CDQ分治 - 线段树 - 树分块
Sean owns a company and he is the BOSS.The other Staff has one Superior.every staff has a loyalty an ...
- 【BZOJ】4129: Haruna’s Breakfast 树分块+带修改莫队算法
[题意]给定n个节点的树,每个节点有一个数字ai,m次操作:修改一个节点的数字,或询问一条树链的数字集合的mex值.n,m<=5*10^4,0<=ai<=10^9. [算法]树分块+ ...
- 【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法
[题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的 ...
- 【bzoj1086】[SCOI2005]王室联邦 树分块
题目描述 将一棵n个点的树分为若干“块”,每个块满足:大小在B到3B之间,并且这个“块”添加某个点后连通.求方案. 输入 第一行包含两个数N,B(1<=N<=1000, 1 <= B ...
- 种树 by yoyoball [树分块+bitset]
题面 给定一棵树,有点权 每次询问给出一些点对,求这些点对之间的路径的并集上不同权值的个数,以及这些权值的$mex$ 思路 先考虑只有一对点对,只询问不同权值个数的问题:树上莫队模板题 然后加个$me ...
- BZOJ1086 王室联邦 —— 树分块
题目链接:https://vjudge.net/problem/HYSBZ-1086 1086: [SCOI2005]王室联邦 Time Limit: 10 Sec Memory Limit: 16 ...
- 洛谷.2590.[ZJOI2008]树的统计(树分块)
题目链接 Update:这种分块写法...可以被卡掉啊... 好像没有靠谱的树分块写法... /* 对树上节点进行分块,每个点记录dep,fa,val,Max,Sum,Max,Sum表示当前点在该块内 ...
- BZOJ.3720.Gty的妹子树(树分块)
题目链接 洛谷上惨遭爆零是为什么.. 另外这个树分块算法是假的. /* 插入删除只涉及一个数,故每次可以枚举一遍,而不是重构完后sort */ #include<cmath> #inclu ...
随机推荐
- 【工程实践】go语言实现MerkleTree
简介 默克尔树(MerkleTree)是一种典型的二叉树结构,其主要特点为: 最下面的叶节点包含存储数据或其哈希值: 非叶子节点(包括中间节点和根节点)的内容为它的两个孩子节点内容的哈希值. 所以底层 ...
- Oracle数据库下的DDL、DML、DQL、TCL、DCL
首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1 ...
- postgresql数据库清理
大量update或者delete后 磁盘空间会猛增.原理是postgresql并没有真正的删除 只是将删除数据的状态置为已删除,该空间不能记录被从新使用.若是删除的记录位于表的末端,其所占用的空间将会 ...
- Unity HDRP BentNormal的理解
1.通过网上冲浪了解到,BentNormal可以解决间接环境高光漏光及间接漫反射光照漏光的问题. 这里的漏光是指间接光照部分没有考虑到模型自身的遮挡关系导致的漏光. 2.可以通过SD之类的软件烘焙Be ...
- 数据库—ER模型概念设计
文章目录 ER模型的概念 如何画ER图 ER图转换为关系数据库 ER模型的概念 实体 画图时用方形表示 属性 用椭圆形表示 关系 用菱形表示 主键(主码) 在主属性下面画划线 外键(外码) 这里一般是 ...
- openstack以后需要研究一下的知识
1. openvt是一个用于在虚拟终端上启动程序的命令行工具.它允许用户在一个新的虚拟终端(VT)上启动一个程序,并将标准输入.输出和错误输出定向到该终端. openvt的用法如下: 打开一个虚拟终端 ...
- Hello Laravel! 准备
Hello Laravel! 准备 目录 Hello Laravel! 准备 什么是 Laravel? 为什么选择 Laravel? 优雅的语法 丰富的功能 强大的社区支持 安全性 易于扩展 Lara ...
- C# winfrom 局域网版多人成语接龙(一)
在学习 springjdbc+c3p0时做了一个数据库版的获得给定词汇的成语接龙,这个做了之后,我突然就想做一个可供多人游戏的成语接龙游戏,由于自己根本不熟悉java的图形界面开发,感觉没有winfo ...
- centos7下启动Django项目报错(sqlite错误)
报错内容如下: [root@localhost project]# python3 manage.py runserver Watching for file changes with StatRel ...
- go encoding/json 替代者
https://github.com/json-iterator/go 可以替代官方包encoding/json 提升json编码和解码效率