启示

  1. 无输出不一定是死循环,也可能是函数类型错误(void-->int)
  2. LCA中两个节点一起往上跳的时候,要循环到0。
  3. LCA里面fa[x][i]啊

tarjan求LCA

走过且走过其子树的点为2,走过但未走过其子树的点为1,为走过的点为0。
走到一个的y,若x是2,则x的第一个为1的祖先为lca。

欧拉序

时间戳:按照dfs遍历,节点被访问的顺序
dfs序:
欧拉:dfs经过的节点顺序。
求LCA:找欧拉序[dfn[u],dfn[v]]中深度最小的节点。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int dfn[N],ol[N],d[N],a[N];
int cnt=0;
int st[N][22];
struct node{
	int v,nxt;
}e[N];
int h[N],tot;
int n,m,s;
void add(int x,int y){
	e[++tot].nxt=h[x];
	e[tot].v=y;
	h[x]=tot;
}
void dfs(int u,int u_fa){
	dfn[u]=++cnt;
	a[cnt]=u;
	d[u]=d[u_fa]+1;
	for(int i=h[u];~i;i=e[i].nxt){
		int v=e[i].v;
		if(v==u_fa) continue;
		dfs(v,u);
		a[++cnt]=u; 
	}
}
void sttt(){
	for(int i=1;i<=cnt;i++) st[i][0] = a[i];
	int h=log(cnt)/log(2)+1;
	for(int i = 1;i <= h;i++){
		for(int j=1;j+(1<<i)-1 <= cnt;j++){
			int u,v;
			u=st[j][i-1],v=st[j+(1<<(i-1))][i-1];
			st[j][i] = (d[u]<d[v])?u:v;
		}
	}
}
int lca(int u,int v){
	if(dfn[u]>dfn[v]) swap(u,v);
	int L=dfn[u],R=dfn[v];
	int k=log(R-L+1)/log(2);
	int p1=st[L][k],p2=st[R-(1<<k)+1][k];
	return d[p1]<d[p2]?p1:p2;
}
int main(){
	memset(h,-1,sizeof h);
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(s,0);
	sttt();
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

The merchant

题意:

\(n\) 个城市构成一棵树,每个城市一个权值表示买入或卖出的值,查询多条路径(x到y)买入物品再卖出的最大利润(只能买入卖出一次且一个物品),若小于0则输出0。\(n,w_i,q\le5000\)

思路:

分为三种,答案在向上路径,向下路径,向下max-向上min。直接维护四个值,最大值,最小值,向上最大值,向下最大值。维护方式和lca相似。可以直接一起算。
细节上举个例子:对于向上路径,向上的最大值就是\(up[u][i],up[fa[u][i-1]][i],mx[fa[u][i-1]][i-1]-mi[u][i-1]\)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=5e4+5;
const int INF=0x3f3f3f3f;
int n,m,s;
struct node{
	int v,nxt;
}e[N<<1];
int a[N];
int d[N],fa[N][20],mx[N][20],mi[N][20],up[N][20],dn[N][20];
int tot,h[N];
void add(int x,int y){
	e[++tot].nxt=h[x];
	e[tot].v=y;
	h[x]=tot;
}
void dfs(int u,int u_fa){
	d[u]=d[u_fa]+1;
	for(int i=h[u];~i;i=e[i].nxt){
		int v=e[i].v;
		if(v==u_fa) continue;
		fa[v][0]=u;
		mx[v][0]=max(a[v],a[fa[v][0]]);
		mi[v][0]=min(a[v],a[fa[v][0]]);
		up[v][0]=max(0,a[fa[v][0]]-a[v]);
		dn[v][0]=max(0,a[v]-a[fa[v][0]]);
		dfs(v,u);
	}
}
void fa_fa(){
	for(int i=1;i<20;i++)
		for(int j=1;j<=n;j++){
			fa[j][i]=fa[fa[j][i-1]][i-1];
			mi[j][i]=min(mi[j][i-1],mi[fa[j][i-1]][i-1]);
			mi[j][i]=max(mi[j][i],0);
			mx[j][i]=max(mx[j][i-1],mx[fa[j][i-1]][i-1]);
			int mm=fa[j][i-1];
			up[j][i]=max(up[j][i-1],max(up[mm][i-1],mx[mm][i-1]-mi[j][i-1]));
			up[j][i]=max(up[j][i],0);
			dn[j][i]=max(dn[j][i-1],max(dn[mm][i-1],mx[j][i-1]-mi[mm][i-1]));
			dn[j][i]=max(dn[j][i],0);
		}
}
int lca(int x,int y){//x->y
	int mii,mxx,um,dm,fl=0;
	mxx=um=dm=0;
	mii=INF;
	if(d[x]<d[y]) swap(x,y),fl=1;
	for(int i=19;i>=0;i--){
		if(d[fa[x][i]]>=d[y]) {
			//fl==0 x->y
			if(!fl){
				um=max(max(um,up[x][i]),mx[x][i]-mii);
				mii=min(mii,mi[x][i]);
			}else{
				dm=max(max(dm,dn[x][i]),mxx-mi[x][i]);
				mxx=max(mxx,mx[x][i]);
			}	
			x=fa[x][i];
		}
	}
	if(x==y) {
		return max(um,max(dm,mxx-mii));	
	}
	for(int i=19;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			if(!fl){
				um=max(max(um,up[x][i]),mx[x][i]-mii);
				mii=min(mii,mi[x][i]);
				dm=max(max(dm,dn[y][i]),mxx-mi[y][i]);
				mxx=max(mxx,mx[y][i]);
			}else{
				um=max(max(um,up[y][i]),mx[y][i]-mii);
				mii=min(mii,mi[y][i]);
				dm=max(max(dm,dn[x][i]),mxx-mi[x][i]);
				mxx=max(mxx,mx[x][i]);
			}
			x=fa[x][i],y=fa[y][i];
		}
	}
	int i=0;
	if(!fl){
		um=max(max(um,up[x][i]),mx[x][i]-mii);
		mii=min(mii,mi[x][i]);
		dm=max(max(dm,dn[y][i]),mxx-mi[y][i]);
		mxx=max(mxx,mx[y][i]);
	}else{
		um=max(max(um,up[y][i]),mx[y][i]-mii);
		mii=min(mii,mi[y][i]);
		dm=max(max(dm,dn[x][i]),mxx-mi[x][i]);
		mxx=max(mxx,mx[x][i]);
	}
	return max(um,max(dm,mxx-mii));
}
int main(){
	memset(mi,0x3f,sizeof mi);
	memset(h,-1,sizeof h);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	dfs(1,0);
	fa_fa();
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		int z=lca(x,y);
		printf("%d\n",max(z,0));
	}
	return 0;
}

注意

  • LCA最后一步虽然只需要求fa[x][0],但对于 \(x\)\(y\) 处的值都需要继续维护。

P2597 [ZJOI2012] 灾难

code:
MYJ_aiie

[HEOI2016/TJOI2016] 树

题意:
一棵树,两种操作。Q:查询某个结点最近的一个打了标记的祖先。C:对某个结点打上标记。(初始只有结点 1 有标记,其他无标记,而且对于某个结点,可以打多次标记。)\(N,Q\leq100000\)
思路:
打标记需要维护的值太多,我们直接逆序操作,删除标记的过程可以看作并查集合并。将标记删为0的节点的最近标记祖先变为上方最近的标记祖先。查询就是找祖先。
code:MYJ_aiie
注意

  1. 初始时的值也要处理。

P1600 [NOIP 2016 提高组] 天天爱跑步

题意:
一棵树。有 \(m\) 个玩家,第 \(i\) 个玩家第 \(0\) 时刻出发从 \(s_i\) 走到 \(t_i\) 。树上每个节点有一个观测员,第 \(i\) 个观测员只有在 \(w_i\) 可以观测。

posted @ 2025-05-25 21:52  MoYujing  阅读(8)  评论(0)    收藏  举报