首页 > 其他分享 >树链剖分学习笔记(未完)

树链剖分学习笔记(未完)

时间:2022-10-03 17:55:54浏览次数:78  
标签:剖分 int top 笔记 树链 dep edges 节点 id

思想

树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。
具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。

重链剖分

原理

首先先定义一些概念

概念 定义
重儿子 每个点的子树中,子树的节点数和最大的子节点
轻儿子 除重儿子外的其他子节点
重边 每个节点与其重儿子间的边
轻边 每个节点与其轻儿子间的边
重链 重边连成的链 (一个点也可以看作是重链)
轻链 轻边连成的链

因此我们可以将一颗树上的所有节点划分到若干条重链上,如图(图源:OiWiki)
image
如右图所示,整棵树被分为一条一条的重链,我们可以在dfs预处理的时候优先处理重儿子,这样可以保证每一条重链上的点的dfs序是连续的,这样就可以把树上的问题转换成区间问题,利用其他处理区间的数据结构来处理树上问题

实现

预处理

int dep[N],fa[N],sz[N],son[N],top[N];
int id[N],nw[N],timestamp;
void dfs1(int u,int father)
{
    dep[u] = dep[father] + 1,sz[u] = 1,fa[u] = father;
    for(int i = h[u];~ i;i = ne[i])
    {
        int j = e[i];
        if(j == fa[u]) continue;
        dfs1(j,u);
        sz[u] += sz[j];
        if(sz[son[u]] < sz[j]) son[u] = j;
    }

}

void dfs2(int u,int t)
{
    id[u] = ++ timestamp;
    nw[id[u]] = w[u];
    top[u] = t;
    if(!son[u]) return ;
    dfs2(son[u],t);
    for(int i = h[u];~ i;i = ne[i])
    {
        int j = e[i];
        if(j == fa[u] || j == son[u]) continue;
        dfs2(j,j);
    }
}

操作链

主要思想:每次操作两点中重链顶点深度较深的一个,然后给他跳到上面的链上去,类似于最近公共祖先暴力跳的步骤,直到两点在一个链上,最后处理两点间的节点

void modify_path(int u,int v,int k)
{
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u,v);
        modify(1,id[top[u]],id[u],k);
        u = fa[top[u]];
    }

    if(dep[u] < dep[v]) swap(u,v);
    modify(1,id[v],id[u],k);
}

LL query_path(int u,int v)
{
    LL res = 0;
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u,v);
        res += query(1,id[top[u]],id[u]);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u,v);
    res += query(1,id[v],id[u]);
    return res;

}

例题

Beard Graph

题意:
给定一棵 n 个节点的树,初始所有边都是黑边。
有 m 个操作:
1 u:把第 u 条边改成黑边。
2 u:把第 u 条边改成白边。
3 u v:若 u 号节点和 v 号节点间存在白边,输出 -1,否则输出 u 号节点和 v 号节点间的黑边数。
分析 :这里涉及一个小tips,题目给的是边,可我们是对点进行剖分,这里可以将每条边的边权设给它连接的两个点之间深度较深的点
将树进行树链剖分,每条边的边权初始为0代表黑边,拿线段树维护即可
对于操作1、2
线段树单点修改即可
对于操作3
进行跳边查询,看有没有白边
这里注意最后处理同一条链的一段时,深度较浅的那个点不考虑,因为我们把每条边赋给了深度较深的点,这个点所代表的表不在我们要求的范围内
ac代码


int n,m,k,t;
int h[N],e[N << 1],ne[N << 1],idx;
int sz[N],fa[N],dep[N],son[N];
int id[N],top[N],nw[N],w[N],x[N],timestamp;
struct node
{
	int l,r;
	int sum ;
}tr[N << 2];

void pushup(int u)
{
	tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void build(int u,int l,int r)
{
	if(l == r)
	{
		tr[u] = {l,r,nw[r]};
		return ;
	}
	tr[u] = {l,r};
	int mid = l + r >> 1;
	build(u << 1,l,mid), build(u << 1 | 1,mid + 1,r);
}

void modify(int u,int x,int v)
{
	if(tr[u].l == tr[u].r)
	{
		tr[u].sum = v;
		return ;
	}
	int mid = tr[u].l + tr[u].r >> 1;
	if(x <= mid) modify(u << 1,x,v);
	else modify(u << 1 | 1,x,v);
	pushup(u);
}

int query(int u,int l,int r)
{
	if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
	int mid = tr[u].l + tr[u].r >> 1;
	int res = 0;
	if(l <= mid) res += query(u << 1,l,r);
	if(r > mid) res += query(u << 1 | 1,l,r);
	return res;
}

void add(int a,int b,int c)
{
	e[idx] = b,x[idx] = c,ne[idx] = h[a],h[a] = idx ++;
}

void dfs1(int u,int father)
{
	sz[u] = 1,fa[u] = father,dep[u] = dep[fa[u]] + 1;
	for(int i = h[u];~ i;i = ne[i])
	{
		int j = e[i];
		if(j == fa[u]) continue;
		w[j] = x[i];
		dfs1(j,u);
		sz[u] += sz[j];
		if(sz[son[u]] < sz[j]) son[u] = j;
	}

}

void dfs2(int u,int t)
{
	id[u] = ++ timestamp;
	nw[timestamp] = w[u];
	top[u] = t;
	if(!son[u]) return ;
	dfs2(son[u],t);
	for(int i = h[u];~ i;i = ne[i])
	{
		int j = e[i];
		if(j == fa[u] || j == son[u]) continue;
		dfs2(j,j);
	}
}
int ans;
int query_path(int u,int v)
{
	int res = 0;
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]]) swap(u,v);
		res += query(1,id[top[u]],id[u]);
		ans += id[u] - id[top[u]] + 1;
		u = fa[top[u]];
	}
	if(dep[u] < dep[v]) swap(u,v); 
	res += query(1,id[son[v]],id[u]);
	ans += id[u] - id[v] + 1;
	return res;
}

int main()
{
	ios;
	cin >> n;	
	for(int i = 1;i <= n;i ++) h[i] = -1;
	vector<PII> edges(n);
	for(int i = 1;i < n;i ++)
	{
		int a,b;
		cin >> a >> b;
		edges[i] = {a,b};
		add(a,b,0), add(b,a,0);
	}

	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	cin >> m;
	while(m --)
	{
		int op,u,v;
		cin >> op ;
		if(op == 1)
		{
			cin >> u ;
			if(dep[edges[u].x] > dep[edges[u].y]) modify(1,id[edges[u].x],0);
			else modify(1,id[edges[u].y],0);
		}
		else if(op == 2)
		{
			cin >> u ;
			if(dep[edges[u].x] > dep[edges[u].y]) modify(1,id[edges[u].x],1);
			else modify(1,id[edges[u].y],1);
		}
		else
		{
			cin >> u >> v;
			if(u == v) 
			{
				cout << 0 << endl;
				continue;
			}
			ans = 0;
			int t = query_path(u,v);
			if(t) cout << -1 << endl;
			else
			{
				cout << max(ans - 1,0) << endl;
			}
		}
	}
	return 0;
}

标签:剖分,int,top,笔记,树链,dep,edges,节点,id
From: https://www.cnblogs.com/notyour-young/p/16750888.html

相关文章

  • 信安系统学习笔记五
    第十一章EXT2文件系统一.知识点归纳EXT2文件系统TheSecondExtendedFileSystem(ext2)文件系统是Linux系统中的标准文件系统,是通过对Minix的文件系统进行扩展而得到......
  • Jenkins 20220927笔记本4
                          ......
  • Jenkins 20220929笔记本5
                                  ......
  • Jenkins 20220925笔记本3
                                                 ......
  • Contrastive Learning for Cold-Start Recommendation阅读笔记
    动机本文是2021年ACMMM上的一篇论文。之前关于推荐系统冷启动的工作很多都使用神经网络来探索冷物品的特征内容和协同表示之间的联合效应,但是作者认为这些工作很少探索内......
  • 20201206韩进学习笔记5
    EXT2文件系统EXT2文件系统数据结构通过mkfs创建虚拟磁盘在Linux下,命令mke2fs[-bblksize-Ninodes]deviceblocks在设备上创建一个带有nblocks个块和inode......
  • P3384 【模板】轻重链剖分/树链剖分
    【模板】轻重链剖分/树链剖分题目描述如题,已知一棵包含\(N\)个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:1xyz,表示将树从\(x\)到\(y\)结点......
  • 学习笔记-SQL报错注入
    报错注入的前提条件:Wed应用程序未关闭数据库报错函数,对于一些SQL语句的错误直接回显在页面上后台未对一些具有报错功能的函数(extractvalue,updataxml)过滤Xpath......
  • 老是记不住单行多行超出省略,这里记笔记
    单行显示:p{width:200px;white-space:nowrap;//使文本单行显示text-overflow:ellipsis;//多余的部分用省略号来代替overflow:hidden;//隐藏多余的部分//单行显示并......
  • 操作系统错题笔记
    “访管”指令仅在用户态下使用,执行“访管”指令将用户态转变为核心态。因操作系统不允许用户直接执行某些“危险性高”的指令,因此用户态运行这些指令的结果会转成操作系......