CodeForces - 609E Minimum spanning tree for each edge (MST+LCA[树上倍增实现])(次小生成树思想)

题目链接:https://codeforces.com/problemset/problem/609/E

题意:n个点m条边,对于每条边,求包含这条边的MST的权重。

思路:首先求出一个MST,假设其权重为sum。对于每条边,如果其在MST上,那么答案就是sum;否则,我们在求得的MST加上这条边(假设权重为w1),那么树出现了环。我们要在树上u->v的路径中删除权值最大的一条边(假设为w2),那么答案就是sum-w1+w2。求次小生成树也是这种思想。

实现1:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
//Kruscal求最小生成树部分 
struct node{
	int id,from,to,w;
}g[N];
vector<node> tg[N];
int n,m,f[N],limit;
ll ans[N],sum=0;
bool book[N];
int getf(int x){ return x==f[x]?x:f[x]=getf(f[x]); }
void add(int u,int v,int w){
	tg[u].push_back(node{0,u,v,w});
	tg[v].push_back(node{0,v,u,w});
}
bool cmp(node a,node b){
	return a.w<b.w;
}
//树上倍增求LCA部分 
int dep[N],dp[N][30],fa[N][30];
//dp[i][j]:代表从i向上走(1<<j)步的路径中最大的边权。
//fa[i][j]: 代表从i向上走(1<<j)步所到的点。
void dfs(int u,int fath,int deep,int w){
	fa[u][0]=fath;
	dp[u][0]=w;
	dep[u]=deep;
	int siz=tg[u].size();
	for(int i=0;i<siz;i++){
		int v=tg[u][i].to;
		if(v==fath) continue;
		dfs(v,u,deep+1,tg[u][i].w);	
	}
	return ;	
}
int lca(int x,int y){
	int maxx=0;
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[fa[x][i]]>=dep[y]){
			maxx=max(maxx,dp[x][i]),x=fa[x][i];	
		}     
	if(x==y) return maxx;	
	for(int i=20;i>=0;i--)
	   if(fa[x][i]!=fa[y][i])
	    maxx=max(maxx,dp[x][i]),maxx=max(maxx,dp[y][i]),x=fa[x][i],y=fa[y][i];
	maxx=max(maxx,dp[x][0]),maxx=max(maxx,dp[y][0]);
	return maxx;	
}
int main(void){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) f[i]=i; 
	for(int i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		g[i]=node{i,u,v,w};
	}	
	sort(g+1,g+1+m,cmp);
	for(int i=1;i<=m;i++){
		int fu=getf(g[i].from),fv=getf(g[i].to);
		if(f[fu]!=f[fv]){
			f[fu]=fv;
			add(g[i].from,g[i].to,g[i].w);
			sum+=g[i].w;
			book[i]=1;
		}		
	}
	dfs(1,-1,1,0);
	for(int i=1;(1<<i)<=n;i++)
	  	for(int j=1;j<=n;j++){
	  		dp[j][i]=dp[j][i-1];//这一步至关重要 ,赋初值。 
			fa[j][i]=fa[fa[j][i-1]][i-1];
	  		dp[j][i]=max(dp[j][i],dp[fa[j][i-1]][i-1]);			  
	  	}
	for(int i=1;i<=m;i++)
		if(book[i]) ans[g[i].id]=sum;
		else ans[g[i].id]=sum-lca(g[i].from,g[i].to)+g[i].w;
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	
	return 0;	
} 

实现2:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
//Kruscal求最小生成树部分 
struct node{
	int id,from,to,w;
}g[N];
vector<node> tg[N];
int n,m,f[N],limit;
ll ans[N],sum=0;
bool book[N];
int getf(int x){ return x==f[x]?x:f[x]=getf(f[x]); }
void add(int u,int v,int w){
	tg[u].push_back(node{0,u,v,w});
	tg[v].push_back(node{0,v,u,w});
}
bool cmp(node a,node b){
	return a.w<b.w;
}
//树上倍增求LCA部分 
int dep[N],dp[N][30],fa[N][30];
//dp[i][j]:代表从i向上走(1<<j)步的路径中最大的边权。
//fa[i][j]: 代表从i向上走(1<<j)步所到的点。
void dfs(int u,int fath,int deep,int w){
	fa[u][0]=fath;
	dp[u][0]=w;
	dep[u]=deep;
  	for(int j=1;(1<<j)<=n;j++){
  		dp[u][j]=dp[u][j-1];//这一步至关重要 ,赋初值。 
		fa[u][j]=fa[fa[u][j-1]][j-1];
  		dp[u][j]=max(dp[u][j],dp[fa[u][j-1]][j-1]);			  
  	}	
	int siz=tg[u].size();
	for(int i=0;i<siz;i++){
		int v=tg[u][i].to;
		if(v==fath) continue;
		dfs(v,u,deep+1,tg[u][i].w);	
	}
	return ;	
}
int lca(int x,int y){
	int maxx=0;
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[fa[x][i]]>=dep[y]){
			maxx=max(maxx,dp[x][i]),x=fa[x][i];	
		}     
	if(x==y) return maxx;	
	for(int i=20;i>=0;i--)
	   if(fa[x][i]!=fa[y][i])
	    maxx=max(maxx,dp[x][i]),maxx=max(maxx,dp[y][i]),x=fa[x][i],y=fa[y][i];
	maxx=max(maxx,dp[x][0]),maxx=max(maxx,dp[y][0]);
	return maxx;	
}
int main(void){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) f[i]=i; 
	for(int i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		g[i]=node{i,u,v,w};
	}	
	sort(g+1,g+1+m,cmp);
	for(int i=1;i<=m;i++){
		int fu=getf(g[i].from),fv=getf(g[i].to);
		if(f[fu]!=f[fv]){
			f[fu]=fv;
			add(g[i].from,g[i].to,g[i].w);
			sum+=g[i].w;
			book[i]=1;
		}		
	}
	dfs(1,0,1,0);
	for(int i=1;i<=m;i++)
		if(book[i]) ans[g[i].id]=sum;
		else ans[g[i].id]=sum-lca(g[i].from,g[i].to)+g[i].w;
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	
	return 0;	
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值