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

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. Swift中的Tuple类型

    Swift中的Tuple类型可以包含任何值,并且这些值的类型可以互相不一样.Tuple本身比较简单,需要记得也就是访问Tuple的方式. 使用变量名访问 let http404Error = (404 ...

  2. 一个list分成 list长度/step_length 向上取整个小list集合

    一.具体实现方法 /** * 将一个list按照新的步长分成list长度/step_length 向上取整个小list * @param list * @param step_length * @re ...

  3. 阿里DataX极简教程

    目录 简介 工作流程 核心架构 核心模块介绍 DataX调度流程 支持的数据 实践 下载 环境 执行流程 引用 简介 DataX是一个数据同步工具,可以将数据从一个地方读取出来并以极快的速度写入另外一 ...

  4. 推荐一款模拟浏览器自动化操作神器!Mechanize

    大家好,我是狂师! 今天给大家推荐一款用于模拟浏览器行为以进行网页自动化操作Python库:Mechanize. 1.介绍 Mechanize是Python中的一个库,它被设计用来自动化网页浏览和数据 ...

  5. log4j日志记录级别

    目录 一.日志的作用 二.log4j的日志级别和简介 三.log4j配置文件包含的节点简介 四.logger配置说明 一.日志的作用 ​ 问题追踪:通过日志不仅仅包括我们程序的一些 bug,也可以在安 ...

  6. Linux 系统用户登录时很慢怎么办

    第一步:编辑 /etc/ssh/sshd_config 文件 vim /etc/ssh/sshd_config 第二步:搜索 DNS 第三步: 将UseDNS前面的#注释删掉,同时将UseDNS后面的 ...

  7. Python爬图片

    1 import requests 2 from lxml import etree 3 4 header = { 5 "user-agent": "Mozilla/5. ...

  8. Android 13 - Media框架(26)- OMXNodeInstance(三)

    关注公众号免费阅读全文,进入音视频开发技术分享群! 上一节我们了解了OMXNodeInstance中的端口定义,这一节我们一起来学习ACodec.OMXNode.OMX 组件使用的 buffer 到底 ...

  9. 滴滴面试:谈谈你对Netty线程模型的理解?

    Netty 线程模型是指 Netty 框架为了提供高性能.高并发的网络通信,而设计的管理和利用线程的策略和机制. Netty 线程模型被称为 Reactor(响应式)模型/模式,它是基于 NIO 多路 ...

  10. 使用 nsenter 排查容器网络问题

    需求 我想进入容器中执行 curl 命令探测某个地址的连通性,但是容器镜像里默认没有 curl 命令.我这里是一个内网环境不太方便使用 yum 或者 apt 安装,怎么办? 这个需求比较典型,这里教大 ...