【图论】基环树

参考@TiAmoZhang 的帖子
参考@I_LOVE_MATH 的文章

概念

一个环上面挂了很多子树,有n个节点和n条边的图 (如果不保证连通的话,那么整张图是一张基环树森林)

并且如果将环上的任意一条边去除,那么整棵基环树会成为一棵普通的树。

第一步找环

  • 有向直接DFS,回到根就是一个环
  • 无向可以使用并查集,不会有重边,所以有集内的边就形成了环
    之后可以把环上任意一条边去除变树,转化为树上问题

典:P2607 [ZJOI2008] 骑士

代码建有向图 DFS找环

cpp 复制代码
const int N = 1e6+10,M=1e5;
const double PI=acos(-1),eps=1e-12;
const long long mod =998244353, inf = 1e18+10 ,up=1e9;
int a[N],vis[N];
vector<int>g[N];// 有向边

/*
直观来想 答案和树的深度有关
找环 以环上两点分别作为根 去掉环上一条边形成树 会有两种答案(奇/偶深度) 环上其他边去掉也一样
树形dp找最大的答案
*/
int r1,r2;
void dfs(int now,int rt){// dfs找环
	vis[now]=1;
	for(auto x:g[now]){
		if(x==rt){
			r1=x,r2=now;
			return;
		}
		if(vis[x])continue;
		dfs(x,rt);
	}
}
int dp[N][2];
int dfs2(int now,int rt){// 树形dp
	// vis2[now]=1;
	
	dp[now][1]=a[now];
	dp[now][0]=0;
	for(auto x:g[now]){
		if(x==rt)continue;// 因为一个联通集要跑两边 不用vis记号了 直接看根来判环
		dfs2(x,rt);
		// += 算总子树和
		dp[now][0]+=max(dp[x][0],dp[x][1]);// 本次不选 子树可选可不选
		dp[now][1]+=dp[x][0];// 本次选 孩子不选
	}
	return dp[now][0];// 直接用函数执行后的返回值 因为每次dfs2都会改变dp
}
void solve(){
    int n;cin>>n;
	forr(i,1,n){
		int hate;
		cin>>a[i]>>hate;
		g[hate].push_back(i);
	}
	int sm=0;
	forr(i,1,n){
		if(vis[i])continue;
		/*
			//一定会找到环的吧
			dfs(i,i);
			cout<<r1<<' '<<r2<<endl;
			sm+=max(dfs2(r1,r1),dfs2(r2,r2));//r1 r2不能同时选 	
			不一定 可能这个点在环外 但是和环联通 
			在之前找环的时候可能遍历不到 从这个点出发也找不到之前找完的环
			如 2 1 2 2的3、4点
		*/
		
		r1=r2=0;
		dfs(i,i);
		// cout<<r1<<' '<<r2<<endl;
		if(r1){
			dfs2(r1,r1);
			sm+=max(dfs2(r1,r1),dfs2(r2,r2));//r1 r2不能同时选 返回的是dp[rt][0]
		}
	}
	cout<<sm<<endl;
}

2025ZJCPC L. Nailoongs Always Lie

有自环,但是对答案无贡献,建有向图,找环方法用了并查集,答案找法类似上面典题。

cpp 复制代码
const int N = 1e5+10,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 2e18 ;
vector<int>g[N];
// 并查集找环
int fa[N],vis[N],cnt[N][2];// cnt[][1奶龙/0不是]
int findf(int x){
   return fa[x]=(fa[x]==x?x:findf(fa[x]));
}
int dfs(int now,int f){// 树形dp找答案
   vis[now]=1;
   cnt[now][0]=0;
   cnt[now][1]=1;
   for(auto x:g[now]){
      if(x==f||vis[x])continue;
      dfs(x,now);
      cnt[now][0]+=max(cnt[x][0],cnt[x][1]);
      cnt[now][1]+=cnt[x][0];
   }
   vis[now]=0;// 回复现场
   return cnt[now][0];
}
void solve(){
   /*
      自环 自己说自己是奶龙 必然是说假话的其他生物 
   */
   int n;cin>>n;
   forr(i,1,n)fa[i]=i;
   vector<int>a(n+1);
   vector<pii>cir;
   forr(i,1,n){
      cin>>a[i];
      g[a[i]].push_back(i);
      int fx=findf(a[i]),fy=findf(i);
      if(fx!=fy)fa[fy]=fx;
      else cir.push_back({a[i],i});
   }
   int ans=0;
   for(auto [u,v]:cir){
      ans+=max(dfs(u,0),dfs(v,0));
   }
   cout<<ans<<endl;
}  
相关推荐
德卡先生的信箱2 小时前
算法部署(一)-模型压缩,剪枝,蒸馏的区别
算法·剪枝
WolfGang0073212 小时前
代码随想录算法训练营 Day44 | 图论 part02
算法·图论
minji...2 小时前
Linux 网络套接字编程(三)UDP服务器与客户端实现:Windows与Linux通信,新增字典翻译功能的 UDP 通信
linux·服务器·开发语言·网络·windows·算法·udp
Robot_Nav2 小时前
Hybrid A* 算法文献解读
算法·路径规划·hybrid a
WolfGang0073212 小时前
代码随想录算法训练营 Day41 | 单调栈 part01
算法·动态规划
嘻嘻哈哈樱桃2 小时前
牛客经典101题解题集--二分查找/排序
数据结构·算法·职场和发展
lihihi2 小时前
CF1992F Valuable Cards
算法
Omics Pro2 小时前
癌症亚型分类新型多组学整合框架
大数据·人工智能·python·算法·机器学习·分类·数据挖掘
熬夜敲代码的猫2 小时前
C++:模板精讲
c++·算法·模板