zoukankan      html  css  js  c++  java
  • [图论神器]树链剖分

    用途

    解决树上问题,一般来说如果有查询又有询问就非常的复杂。如果用LCA你更改了树就会原地爆炸,因而树链剖分的用途就显现出来了。虽然板子多,还不如打暴力

    预备概念

    重儿子:父亲结点的所有儿子结点中,子树拥有结点数最多的结点。轻儿子:父亲结点除了重儿子以外的所有儿子。重边:父亲结点与重儿子的连边。轻边:父亲结点与轻儿子的连边。重链:所有重边所组成的一条链。

    变量声明

    fa[u]:保存点u的父亲结点d[u]:保存点u的深度size[u]:保存以u为根节点的子树的结点个数son[u]:保存点u的重儿子top[u]:保存点u所在重链的顶端结点id[u]:保存点u进行重新编号后的新的编号

    操作

    首先进行一次dfs求出除id,top外的数组。

    void dfs1(int step,int fa1){
    	size[step] = 1;
    	for (int i = 0;i < G[step].size();i ++){
    		int u = G[step][i];
    		if (u != fa1){
    			fa[u] = step;
    			dep[u] = dep[step] + 1;
    			dfs1(u,step);
    			size[step] += size[u];
    			if (size[u] > size[son[step]])
    				son[step] = u;
    		}
    	}
    }
    

    然后,在进行一次dfs求出id,top

    void dfs2(int step,int fa1){
    	id[step] = ++ cnt;
    	w1[cnt] = w[step];
    	top[step] = fa1;
    	if (!son[step])
    		return ;
    	dfs2(son[step],fa1);
    	for (int i = 0;i < G[step].size();i ++){
    		int u = G[step][i];
    		if (u != fa[step] && u != son[step])
    			dfs2(u,u);
    	}
    }
    

    然后我就不知道该怎么写了…
    由于很久以前的东西(2019年起稿,快一年了),不想写了,所以直接贴代码,保存一下模板吧

    void solve1(int u,int v,int x){//区间修改
    	while (top[u] != top[v]){
    		if (dep[top[u]] < dep[top[v]])
    			swap(u,v);
    		update(1,x,id[top[u]],id[u]);
    		u = fa[top[u]];
    	}
    	if (dep[u] > dep[v])
    		swap(u,v);
    	update(1,x,id[u],id[v]);
    }
    int solve2(int u,int v){//区间查询
    	int ans = 0;
    	while (top[u] != top[v]){
    		if (dep[top[u]] < dep[top[v]])
    			swap(u,v);
    		ans = (ans + find(1,id[top[u]],id[u])) % p;
    		u = fa[top[u]];
    	}
    	if (dep[u] > dep[v])
    		swap(u,v);
    	ans = (ans + find(1,id[u],id[v])) % p;
    	return ans;
    }
    

    线段树维护

    void build (int i,int l,int r){
    	tree[i].l = l,tree[i].r = r;
    	if(l == r){
    		tree[i].x = w1[l];
    		return ;
    	}
    	int mid = (l + r) / 2;
    	build (i * 2,l,mid);
    	build (i * 2 + 1,mid + 1,r);
    	tree[i].x = (tree[i * 2].x + tree[i * 2 + 1].x) % p;
    }
    void pushdown(int x){
    	tree[x * 2].x = (tree[x * 2].x + (tree[x * 2].r - tree[x * 2].l + 1) * lazy[x] % p) % p;
    	tree[x * 2 + 1].x = (tree[x * 2 + 1].x + (tree[x * 2 + 1].r - tree[x * 2 + 1].l + 1) * lazy[x] % p) % p;
    	lazy[x * 2] = (lazy[x * 2] + lazy[x]) % p; 
    	lazy[x * 2 + 1] = (lazy[x * 2 + 1] + lazy[x]) % p;
    	lazy[x] = 0;
    }
    void update(int i,int x,int l,int r){
    	if (tree[i].l > r || tree[i].r < l)
    		return ;
    	if (tree[i].l >= l && tree[i].r <= r){
    		lazy[i] = (lazy[i] + x) % p;
    		tree[i].x = (tree[i].x + (tree[i].r - tree[i].l + 1) * x) % p;
    		return ;
    	}
    	if (lazy[i])
    		pushdown(i);
    	update(i * 2,x,l,r);
    	update(i * 2 + 1,x,l,r);
    	tree[i].x = (tree[i * 2].x + tree[i * 2 + 1].x) % p;
    }
    int find(int i,int l,int r){
    	if (tree[i].l > r || tree[i].r < l)
    		return 0;
    	if (tree[i].l >= l && tree[i].r <= r)
    		return tree[i].x;
    	else {
    		if (lazy[i])
    			pushdown(i);
    		int sum = 0;
    		sum = (sum + find(i * 2,l,r)) % p;
    		sum = (sum + find(i * 2 + 1,l,r)) % p;
    		return sum;
    	}
    }
    
  • 相关阅读:
    .Net EF中DbContext动态生成DbSet
    .net core 3.0 中间件或过滤器中读取post请求body方法
    Asp.Net Core 5 WebAPI发布后的Swagger不显示问题
    .net Core 使用Swagger 让某些接口不显示在文档
    C# Request.InputStream 读取输入流为空的原因处理
    ASP.NET 中的缓存
    缓存依赖(文件、数据库)
    NLTK基本使用
    NLTK基本使用
    NLTK的基本使用
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566652.html
Copyright © 2011-2022 走看看