写点基础的东西。随便写的,勿喷。

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

  1. \(u\) 未根节点

  2. \(u\) 有至少两个子树中存在 boundary

  3. 栈中剩余边数量大于 \(B\)

好,假若点 \(u\) 成为了 boundary node 接下考虑如何把其子树划进不同的 cluster

我们贪心的在栈中选择极长前缀直至以下情况发生

  1. 子树用完

  2. 新加入一个子树会使当前 cluster 中 boundary 数量超过 \(2\)

  3. 新加入一个子树会使当前 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 树分块的更多相关文章

  1. 【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

    2016-05-09 UPD:学习了新的DFS序列分块,然后发现这个东西是战术核导弹?反正比下面的树分块不知道要快到哪里去了 #include<cmath> #include<cst ...

  2. [BZOJ 1086] [SCOI2005] 王室联邦 【树分块】

    题目链接:BZOJ - 1086 题目分析 这道题要求给树分块,使得每一块的大小在 [B, 3B] 之间,并且可以通过一个块外的节点(块根)使得整个块联通. 那么我们使用一种 DFS,维护一个栈,DF ...

  3. 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 ...

  4. 【BZOJ】4129: Haruna’s Breakfast 树分块+带修改莫队算法

    [题意]给定n个节点的树,每个节点有一个数字ai,m次操作:修改一个节点的数字,或询问一条树链的数字集合的mex值.n,m<=5*10^4,0<=ai<=10^9. [算法]树分块+ ...

  5. 【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法

    [题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的 ...

  6. 【bzoj1086】[SCOI2005]王室联邦 树分块

    题目描述 将一棵n个点的树分为若干“块”,每个块满足:大小在B到3B之间,并且这个“块”添加某个点后连通.求方案. 输入 第一行包含两个数N,B(1<=N<=1000, 1 <= B ...

  7. 种树 by yoyoball [树分块+bitset]

    题面 给定一棵树,有点权 每次询问给出一些点对,求这些点对之间的路径的并集上不同权值的个数,以及这些权值的$mex$ 思路 先考虑只有一对点对,只询问不同权值个数的问题:树上莫队模板题 然后加个$me ...

  8. BZOJ1086 王室联邦 —— 树分块

    题目链接:https://vjudge.net/problem/HYSBZ-1086 1086: [SCOI2005]王室联邦 Time Limit: 10 Sec  Memory Limit: 16 ...

  9. 洛谷.2590.[ZJOI2008]树的统计(树分块)

    题目链接 Update:这种分块写法...可以被卡掉啊... 好像没有靠谱的树分块写法... /* 对树上节点进行分块,每个点记录dep,fa,val,Max,Sum,Max,Sum表示当前点在该块内 ...

  10. BZOJ.3720.Gty的妹子树(树分块)

    题目链接 洛谷上惨遭爆零是为什么.. 另外这个树分块算法是假的. /* 插入删除只涉及一个数,故每次可以枚举一遍,而不是重构完后sort */ #include<cmath> #inclu ...

随机推荐

  1. 01 go-zero入门--单体服务demo

    目录 官方文档: 视频地址: 系统环境: 配置环境: 1. golang安装 2. go module配置 3. goctl安装 4.protoc,protoc-gen-go,protoc-gen-g ...

  2. python—CSV的读写

    目录 csv文件 打开模式 1.写入数组类型数据 2.写入字典序列类型数据 3.csv的读取 csv文件 CSV是一种以逗号分隔数值的文件类型,在数据库或电子表格中,常见的导入导出文件格式就是CSV格 ...

  3. python教程6.2-OS模块random模块

    OS模块  random模块

  4. 一个网格合并(weld)小工具

    在日常开发中会有需求合并多个Mesh网格,并且它们重合处的顶点也要合并,而并非合并成两个subMesh. 而近期刚好在学习Geomipmap的细分,需要把多个mesh块进行合并,于是写了这个脚本 (简 ...

  5. Ubuntu Snap 简述

    Ubuntu Snaps Ubuntu Snaps 是 Ubuntu 的母公司 Canonical 于 2016 年 4 月发布 Ubuntu16.04 LTS(LongTermSupport,长期支 ...

  6. 『手撕Vue-CLI』添加帮助和版本号

    前言 经过上一篇『手撕Vue-CLI』编码规范检查之后,手撕 Vue-CLI 已经进阶到了代码规范检查这一步,已经将基本的工程搭建好了,然后代码规范约束也已经加入了,并且将 nue-cli 指令绑定到 ...

  7. vue4 项目的创建

    1,安装vue cli 脚手架,是全局安装npm install -g @vue/cli 可以在输出信息中看到安装位置,例如:C:\Users\xiaochangjian\AppData\Roamin ...

  8. PHP 网络通信底层原理分析

    一.引言 我们日常的程序开发大多数都是以业务为主,很少会接触到底层逻辑.对于我们程序员来说,了解程序的底层运行逻辑,更有助于提升我们对程序的理解.我相信大多数的人,每天基本上都是完成业务需求.当然,完 ...

  9. HTML——超链接标签

    一.超链接标签的基本使用 超链接是浏览者和服务器的交互的主要手段,也叫超级链接或a链接,是网页中指向一个目标的连接关系,这个目标可以是网页.网页中的具体位置.图片.邮件地址.文件.应用程序等. 超链接 ...

  10. iOS性能优化-内存泄漏

    一.现象 最近直播助手在iOS11 ReplayKit上面经常出现进程使用内存超过50MB被系统杀掉,并且这种进程被杀掉不会有任何crash log留下来,只能在Console中留下一点消息. 二.使 ...