LCA模板解释与树上差分介绍

倍增求LCA

对于树上求两个节点的最小公共祖先,很明显,可以通过从这两个点一步步往上走,得到一个相同的节点,那么这个节点就是所要求的最小公共祖先了。但是,这种暴力,明显会T。那么,既然我们可以通过一步步往上爬,是不是也可以两步两步往上爬,三步三步,甚至一百步一百步呢?
记得杰伦有首歌叫《蜗牛》-----我要一步一步往上爬
既然有这样一种一次往上爬多步的思想在了,那么,距离一次是爬几步呢?我们发现,所有的数都可以由任意个不同的2的幂次方组成,所以,可以每次往上爬不同的2的幂次方。这就是倍增求LCA的主要思想了。这和ST表思想一样。
代码:
// 最终模板
#include 
using namespace std;
const int N=5e5+5,M=5e5+5;
int n,m,s,u,v,a,b;
int d[N],p[N][31];
int cnt,head[N];
struct edge{int next,to;}e[M<<1];

inline void add(int u,int v)
{
    cnt++;
    e[cnt].next=head[u];
    e[cnt].to=v;
    head[u]=cnt;	
}

inline void dfs(int u,int fa)
{
	// p[i][j]表示i节点向上2^j层后到达的节点 
    for (register int i=1; (1<d[b]) swap(a,b);  //强制让b节点向上移动 
    for (register int i=20; i>=0; --i) if (d[b]-(1<=d[a]) b=p[b][i];
    // 如果,位于下面的b节点,向上移动(1<=a的层数的话,就把b节点向上移动 
    if (a==b) return a;
    for (register int i=20; i>=0; --i)
    if (p[a][i]==p[b][i]) continue;    //如果这次的这个祖先一样,就找下一个祖先,写个continue,更有层次感 
    else a=p[a][i],b=p[b][i];          //如果这次的祖先不一样,那么就把这两个点都向上移动 
    return p[a][0];
}

int main(){
    scanf("%d%d%d",&n,&m,&s);
    for (register int i=1; i
学会LCA模板,就可以解决很多的模板了。其中,就有树上差分。

树上点差分

相信大家都知道线性差分的写法、作用与适用范围。差分就是一种能解决修改多次,询问较少次问题的简单数据结构,除了线性差分,还有树上差分。
点差分:给你一棵树,每个节点都有相应的权值,然后给你若干个修改,每次把从u到v的路径上的节点权值都加上w,问最终每个节点权值是多少。
代码:
// 对于点差分来说,每次修改时候公式为:
// f[a]++,f[b]++,f[lca(a,b)]--, f[p[lca(a,b)][0]]--

#include 
using namespace std;
const int N=5e4+5;
int n,m,u,v,x,y,LCA,ans;
int d[N],p[N][21],f[N];
int cnt,head[N];
struct edge{int next,to;}e[N<<1];

inline void add(int u,int v)
{
	cnt++;
	e[cnt].next=head[u];
	e[cnt].to=v;
	head[u]=cnt;		
}

void dfs(int u,int fa)
{
	for (register int i=1; (1<d[b]) swap(a,b);
	for (register int i=20; i>=0; --i) if (d[b]-(1<=d[a]) b=p[b][i];
	if (a==b) return a;
	for (register int i=20; i>=0; --i)
	if (p[a][i]==p[b][i]) continue;
	else a=p[a][i],b=p[b][i];
	return p[a][0];	
}

void dfs1(int u,int fa)
{
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].to!=fa)
	{
		dfs1(e[i].to,u);
		f[u]+=f[e[i].to];
	}
	ans=max(ans,f[u]);
}

int main(){
	scanf("%d%d",&n,&m);
	for (register int i=1; i
推荐题目:
https://www.luogu.org/problemnew/show/P3128
https://www.luogu.org/problemnew/show/P3258
注意,对于一开始的权值,应该先把这个权值记录,在修改的差分数组全部做完后再在答案数组上累加原有的权值。

树上边差分

与点差分大同小异,只要把边权压到点权利即可,原来的f[i]表示节点i的权值,现在的f[i]表示节点i的父亲节点到i节点的距离。其中,f[根]=0。
代码:
#include 
using namespace std;
const int N=5e4+5;
int n,m,u,v,w,x,y,LCA,ans;
int d[N],p[N][21],f[N],ff[N];
int cnt,head[N];
struct edge{int next,to,w;}e[N<<1];

inline void add(int u,int v,int w)
{
	cnt++;
	e[cnt].next=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;		
}

void dfs(int u,int fa,int w)
{
	ff[u]=w;
	for (register int i=1; (1<d[b]) swap(a,b);
	for (register int i=20; i>=0; --i) if (d[b]-(1<=d[a]) b=p[b][i];
	if (a==b) return a;
	for (register int i=20; i>=0; --i)
	if (p[a][i]==p[b][i]) continue;
	else a=p[a][i],b=p[b][i];
	return p[a][0];	
}

void dfs1(int u,int fa)
{
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].to!=fa)
	{
		dfs1(e[i].to,u);
		f[u]+=f[e[i].to];
	}
	ans=max(ans,f[u]);
}

int main(){
	//f[i]表示i节点的父亲到i节点的距离
	scanf("%d",&n);
	for (register int i=1; i
推荐题目:https://www.luogu.org/problemnew/show/P2680

你可能感兴趣的:(LCA,差分)