【算法学习】基环树
1.【算法学习】排序2.【算法学习】Manacher 马拉车3.【算法学习】KMP 算法4.LCA 最近公共祖先(树链和倍增)这次真有树链了!!!5.线段覆盖问题6.【算法学习】学换根dp有感7.二分图最大匹配8.【算法学习】01BFS9.洛谷 P1892 [BOI2003] 团伙 种类并查集 扩展域并查集10.【算法学习】高斯消元法11.贝叶斯公式12.背包13.【算法学习】模拟退火
14.【算法学习】基环树
15.【算法学习】树链部分16.【算法学习】莫队17.【算法学习】分块九讲18.平衡树19.圆方树20.【算法学习】点分治21.公式22.【算法学习】笛卡尔树23.【算法学习】悬线法24.欧几里得算法与 EX25.【算法学习】逆元与求解26.【算法学习】费马定理27.三分28.裴蜀定理29.【算法学习】欧拉函数φ30.【算法学习】二维转一维问题31.【算法学习】扫描线32.【算法学习】矩阵乘法33.【算法学习】同余最短路34.【算法学习】组合数学35.【算法学习】反悔贪心基环树
基环树就是类似于在树上加了一条边形成了环,去点环上的一条边后就会变成数,如下图。
这是一个 \(n\) 个点 \(n\) 条边的连通图,如果不保证联通,它就会成为基环树森林。
外向树:每个点都只有一条入边,因为向内上。
内向树:每个点都只有一条出边,因为向外少。
怎么用呢?
1.把环取出,先对树操作,再对环操作,但是这样明显有时会很复杂。
2.把环上一条边忽视掉,在这条边的两个端点分别操作整棵树。
例题
P2607 [ZJOI2008] 骑士
因为一个骑士只会讨厌另外一个骑士,所以每个连通块都是一颗基环树,所以我们对每颗基环树拆环成树进行树形dp即可。
我们可以把图建成一个有向图,他讨厌的骑士作他的父亲,因为我们环上相邻的两个点都不能选,所以我们先强制不选根节点进行一遍树形dp,状态为f[x][1/0]表示选/不选x节点最大的权值,然后再对根节点的父亲进行一遍树形dp,这样就包含了所有情况了!!
#include <bits/stdc++.h> #define re register const int N=1e6+1e5; #define int long long const int inf=1e9; using namespace std; int n; vector<int> v[N]; int vis[N]; int f[N][3]; int fa[N]; int a[N]; int ans=0; int mx=0; int root=1; void dfs(int x){ vis[x]=1; f[x][0]=0; f[x][1]=a[x]; for(int i:v[x]){ if(i!=root){ dfs(i); f[x][0]+=max(f[i][1],f[i][0]); f[x][1]+=f[i][0]; } else{ f[i][1]=-inf; } } } signed main(){ ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]>>fa[i]; v[fa[i]].push_back(i); } for(int i=1;i<=n;i++){ if(!vis[i]){ vis[i]=1; mx=0; root=i; while(!vis[fa[root]]){ vis[root]=1; root=fa[root]; } vis[root]=1; dfs(root); mx=max(f[root][1],f[root][0]); root=fa[root]; dfs(root); ans+=max(mx,max(f[root][1],f[root][0])); } } cout<<ans; return 0; }
P1453 城市环路
我是看不懂题一点,直接看大佬解释题意了,给出一个 \(N\) 个点 \(N\) 条边的连通图,每个点有点权,求出其最大独立集(不能选取相邻的节点情况下能选取点集的最大点权和)
发现这与上面那题一模一样,没啥意思直接秒掉!!!
如何找到环边可以用并查集找到如果两个点在同一连通块内就说明该点为环边,然后就可以树形dp了,所有为什么我调了15分钟呢???
#include <bits/stdc++.h> #define re register const int N=1e6+1e5; #define int long long const int inf=1e9; using namespace std; int n; vector<int> v[N]; int vis[N]; double f[N][3]; double fa[N]; double k; double a[N]; double ans=0; double mx=0; int root=1; void dfs(int x,int faa){ vis[x]=1; f[x][0]=0; f[x][1]=a[x]; f[root][1]=-inf; for(int i:v[x]){ if(i==faa){ continue; } if(i!=root&&!vis[i]){ dfs(i,x); f[x][0]+=max(f[i][1],f[i][0]); f[x][1]+=f[i][0]; } } } int l,r; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } signed main(){ // ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++){ fa[i]=i; } for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ int u,vv; cin>>u>>vv; u++; vv++; v[u].push_back(vv); v[vv].push_back(u); if(find(u)==find(vv)){ l=u,r=vv; } else{ u=find(u); vv=find(vv); fa[u]=vv; } } cin>>k; mx=0; root=l; dfs(root,0); mx=max(f[l][1],f[l][0]); memset(f,0,sizeof f); memset(vis,0,sizeof vis); root=r; dfs(root,0); ans=max(mx,max(f[r][1],f[r][0])); printf("%.1lf",ans*k); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)