基环树算法总结
1.$CDQ$ 分治总结
2.基环树算法总结
3.(抄自己luogu上的博客)莫队总结4.珂朵莉树总结5.虚树总结6.从龟速乘到 $Miller-Rabin$ 算法(数论算法总结)7.浅谈李超线段树8.浅谈长链剖分9.浅谈平衡树10.可持久化数据结构算法总结11.线段树运用进阶12.群论学习笔记13.线性基学习笔记14.LCT 学习笔记15.原根学习笔记+BSGS复习笔记16.多项式算法初探:从 FFT 到 NTT17.下降幂、斯特林数学习笔记18.多项式算法再探:FMT 和 FWT19.min-max 容斥(最值反演)学习笔记20.自适应 Simpson 积分法学习笔记21.SAM 学习笔记22.min_25 筛 学习笔记基环树算法总结
一、什么是基环树
基环树,顾名思义,有两个要素:环和树。
因此,基环树就是一棵树的一个节点,扩成一个环,做题时,多棵基环树组成的基环树森林,常以如下方式出现:
- 每个点只有一个出边。
- 每个点只有一个入边。
- 图中一共有 \(n\) 个点,\(n\) 条边。
那么,基环树类型的题目应该怎么做呢?
二、基环树怎么做
先来看看这道题:\([ZJOI2008]\) 骑士
此题和没有上司的舞会几乎一模一样,不一样的是,这道题是基环树。
考虑假如这道题在树上就会简单很多,那么我们就会发现,假如断掉环上的一条边,那么原来的基环树就变成了一棵树,树形 dp 即可。
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;
int n,m,a,h[N],w[N];
int to[N*2],nxt[N*2];
int vis[N],fa[N];
int q1[N],q2[N],l,r;
ll dp1[N],dp2[N],ans;
void add(int x,int y){
to[++m]=y;
nxt[m]=h[x];
h[x]=m;
}void dp_(int x,int f,int rt){
vis[x]=1;
dp1[x]=0;
dp2[x]=w[x];
for(int i=h[x];i;i=nxt[i]){
int y=to[i];
if(y==a)
continue;
dp_(y,x,rt);
dp1[x]+=max(dp1[y],dp2[y]);
dp2[x]+=dp1[y];
}
}int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
int j;
cin>>w[i]>>j;
add(j,i);
fa[i]=j;
}for(int i=1;i<=n;i++){
if(vis[i]) continue;
vis[(a=i)]=1;
while(!vis[fa[a]]){
a=fa[a];
vis[a]=1;
}dp_(a,0,a);
ll cc=dp1[a];
a=fa[a];
dp_(a,0,a);
ans+=max(cc,dp1[a]);
}cout<<ans;
return 0;
}
接着我再介绍一下我求基环树(单向图)时常用的找环方法(其中 \(a_i\) 表示 \(i\) 的出/入边与 \(a_i\) 相连):
在进行并查集时,无条件将 \(i\) 置于 \(a_i\) 下。这样可以保证每个根节点都在环上,使判环更便捷、更迅速。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)