1. 裸板子
題目連結:P3379 【模板】最近公共祖先(LCA)
展開程式碼
#include<bits/stdc++.h>
using namespace std;
#define P(x) cout<<#x<<":"<<x<<endl;
#define pb push_back
const int MAXN=int(5e5+5);
int n,m,s;
int lg[MAXN];
int fa[MAXN][22],dep[MAXN];
vector<int> G[MAXN];
void Init_lg() {
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}
void Dfs0(int u,int p) {
dep[u]=dep[p]+1;
fa[u][0]=p;
for(int i=1;(1<<i)<=dep[u];i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(auto v:G[u])
if(v!=p)
Dfs0(v,u);
}
int lca(int u,int v) {
if(dep[u]<dep[v])
swap(u,v);
while(dep[u]>dep[v])
u=fa[u][lg[dep[u]-dep[v]]-1];
if(u==v)
return u;
for(int k=lg[dep[u]];k>=0;k--)
if(fa[u][k]!=fa[v][k])
u=fa[u][k],v=fa[v][k];
return fa[u][0];
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].pb(v),G[v].pb(u);
}
Init_lg();
Dfs0(s,0);
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",lca(u,v));
}
}
2. 提要
2.1 LCA的副產品
- 常見的做法,在處理LCA的同時維護路徑上的其他資訊(如路徑最大值、路徑最小值、路徑元素個數等可合併的資訊)
d(u,v)
即是二者到LCA的距離和(dep[u]-dep[lca])+(dep[v]-dep[lca])
2.2
3. 應用
3.1 維護路徑最小值
題目連結:P9235 [藍橋杯 2023 省 A] 網路穩定性
最大生成樹優先選擇長邊,使路徑上的最小值最大
展開程式碼
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define fi first
#define sd second
typedef long long ll;
typedef pair<int,int> PII;
const int MAXN=int(3e5+5);
int n,m,q;
int fa[MAXN];
vector<PII> G[MAXN];
struct edge {
int u,v,w;
}e[MAXN];
int lg[MAXN],dep[MAXN];
int p[MAXN][30],upm[MAXN][30];
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool cmp(edge a,edge b) {
return a.w>b.w;
}
void Kruscal() {
for(int i=1;i<=n;i++)
fa[i]=i;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++) {
int u=e[i].u,v=e[i].v,w=e[i].w;
int fu=find(u),fv=find(v);
if(fu==fv) continue;
fa[fu]=fv;
G[u].pb({v,w});
G[v].pb({u,w});
}
}
void Dfs0(int u,int pre,int w_down) {
dep[u]=dep[pre]+1;
p[u][0]=pre;
upm[u][0]=w_down;
for(int i=1;(1<<i)<=dep[u];i++) {
p[u][i]=p[p[u][i-1]][i-1];
upm[u][i]=min(upm[u][i-1],upm[p[u][i-1]][i-1]);
}
for(auto t:G[u]) {
int v=t.fi,w=t.sd;
if(v!=pre)
Dfs0(v,u,w);
}
}
void Init_lca() {
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
memset(upm,0x3f,sizeof(upm));
for(int i=1;i<=n;i++)
if(find(i)==i)
Dfs0(i,0,0x3f3f3f3f);
}
int Query(int u,int v) {
if(find(u)!=find(v))
return -1;
if(dep[u]<dep[v]) swap(u,v);
int res=0x3f3f3f3f;
while(dep[u]>dep[v]) {
int d=dep[u]-dep[v];
res=min(res,upm[u][lg[d]-1]);
u=p[u][lg[d]-1];
}
if(u==v)
return res;
for(int k=lg[dep[u]];k>=0;k--)
if(p[u][k]!=p[v][k]) {
res=min(upm[u][k],res);
res=min(upm[v][k],res);
u=p[u][k],v=p[v][k];
}
res=min(upm[u][0],res);
res=min(upm[v][0],res);
return res;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[i]={u,v,w};
}
Kruscal();
Init_lca();
for(int i=1;i<=q;i++) {
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",Query(u,v));
}
}
3.2 維護路徑元素
題目連結:Pxxxx [藍橋杯 2024 省 A] G(未出)
路徑節點顏色總數小 ( $C<=20$ ) ,狀壓維護
展開程式碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
#define pb push_back
#define lowbit(x) (x&-x)
const int MAXN=int(1e5+5);
const int MAXC=int(25);
int n,m;
int col[MAXN];
int lg[MAXN],fa[MAXN][30],C[MAXN][30];
int dep[MAXN];
vector<int> G[MAXN];
void dfs(int u,int p) {
dep[u]=dep[p]+1;
fa[u][0]=p;
C[u][0]=(1<<col[u]);
if(p!=0)
C[u][0]|=(1<<col[p]);
for(int i=1;(1<<i)<=dep[u];i++) {
fa[u][i]=fa[fa[u][i-1]][i-1];
C[u][i]=C[u][i-1]|C[fa[u][i-1]][i-1];
}
for(auto v:G[u])
if(v!=p)
dfs(v,u);
}
void init() {
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
dfs(1,0);
}
int count(int x) {
int res=0;
while(x) {
res++;
x-=lowbit(x);
}
return res;
}
int query(int u,int v) {
if(u==v)
return col[u];
if(dep[u]<dep[v])
swap(u,v);
int cols=0;
while(dep[u]>dep[v]) {
int t=lg[dep[u]-dep[v]]-1;
cols|=C[u][t];
u=fa[u][t];
}
if(u==v)
return count(cols);
for(int i=lg[dep[u]];i>=0;i--)
if(fa[u][i]!=fa[v][i]) {
cols|=(C[u][i]|C[v][i]);
u=fa[u][i],v=fa[v][i];
}
cols|=C[u][0]|C[v][0];
return count(cols);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&col[i]);
for(int i=1;i<=n-1;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].pb(v);
G[v].pb(u);
}
init();
while(m--) {
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",query(u,v));
}
}