【XSY2515】管道(pipe)(最小生成树+倍增lca)

6 篇文章 0 订阅
3 篇文章 0 订阅

题面

Description

给你一个城市下水道网络图,你需要选出一些管道,使得在只使用这些管道的情况下,令整个网络联通,并且花费最小。

网络图可以看做是无向连通图,有 n n n个节点和 m m m条边,每条边连接 u i u_i ui v i v_i vi,选择的花费是 w i w_i wi

不巧的是,由于某些原因,现在市政局要求选定某条特定的边管道,你的任务是求出对于某一条边,在选择这条管道的前提下的最小花费。

Input

1 1 1行包含两个整数 n n n m m m,表示点数和边数。

2 2 2~ m + 1 m+1 m+1行每行三个整数 u i u_i ui v i v_i vi w i w_i wi,表示有一条管道连接 u i u_i ui v i v_i vi,费用为 w i w_i wi

Output

输出m行,每行一个整数,表示选择第i条管道的前提下的最小花费。

管道按输入的顺序编号为 1 1 1~ m m m

Sample Input

5 7
1 2 3
1 3 1
1 4 5
2 3 2
2 5 3
3 4 2
4 5 4

Sample Output

9
8
11
8
8
8
9

Hint

对于 20 % 20\% 20%的数据, n &lt; = 1000 n&lt;=1000 n<=1000 m &lt; = 2000 m&lt;=2000 m<=2000

对于另外 20 % 20\% 20%的数据, m &lt; = n + 10 m&lt;=n+10 m<=n+10

对于 100 % 100\% 100%的数据, 2 &lt; = u i , v i &lt; = n &lt; = 100000 2&lt;=u_i,v_i&lt;=n&lt;=100000 2<=ui,vi<=n<=100000 1 &lt; = m &lt; = 200000 1&lt;=m&lt;=200000 1<=m<=200000 w i &lt; = 2 31 w_i&lt;=2^{31} wi<=231

保证初始图连通。

题解

题目就是求包含某条边的最小生成树。

先把原图的最小生成树求出来。

枚举图上的每一条边,考虑选择这条管道的前提下的最小花费:

  1. 如果这条边就在最小生成树上,显然,最小花费就是最小生成树的边权和。

  2. 如果这条边不在最小生成树上,如下图中的边 ( 5 , 6 ) (5,6) (5,6)

    在这里插入图片描述

    显然如果我们加入了边 ( 5 , 6 ) (5,6) (5,6),就会构成一个环,这个环的一部分就是 ( 5 , 6 ) (5,6) (5,6),另一部分是在树上的 5 5 5 6 6 6的路径,即 5 ⟶ 3 ⟶ 4 ⟶ 6 5\longrightarrow3\longrightarrow4\longrightarrow6 5346

    这样如果我们去掉环上的任意一条边,所有点还是联通的,且边权和比没去时更小。

    又因为我们不能去掉边 ( 5 , 6 ) (5,6) (5,6),所以我们只能去掉原图的最小生成树上的 5 5 5 6 6 6的路径的任意一边。

    且为了保证去掉这条边后边权和最小,我们要去掉这条路径上边权最大的边。

这样两种情况都讨论完了,至于第二种情况的路径上边权最大值怎么维护,可以用树剖或者倍增。

代码如下:

#include<bits/stdc++.h>

#define N 1000010
#define M 2000010
#define int long long
#define ll long long

using namespace std;

struct edge
{
	int u,v,w,id;
}e[M];

int n,m,fa[N];
int f[N][20],maxn[N][20],d[N];
int cnt,head[N],nxt[N<<1],to[N<<1],w[N<<1];
ll ans,Ans[N];
bool flag[N];

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<3)+(x<<1)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int find(int x)
{
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}

inline bool cmp(edge a,edge b)
{
	return a.w<b.w;
}

inline void adde(int u,int v,int wi)
{
	to[++cnt]=v;
	w[cnt]=wi;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

void dfs(int u)
{
	for(int i=1;i<=18;i++)//倍增数组
	{
		f[u][i]=f[f[u][i-1]][i-1];
		maxn[u][i]=max(maxn[u][i-1],maxn[f[u][i-1]][i-1]);
	}
	for(int i=head[u];i;i=nxt[i])
	{
		if(to[i]!=f[u][0])
		{
			f[to[i]][0]=u;
			maxn[to[i]][0]=w[i];
			d[to[i]]=d[u]+1;
			dfs(to[i]);
		}
	}
}

inline int LCA(int a,int b)//a->lca->b
{
	int maxx=0;
	if(d[a]<d[b])
		swap(a,b);
	for(int i=18;i>=0;i--)
		if(d[f[a][i]]>=d[b])	
			maxx=max(maxx,maxn[a][i]),a=f[a][i];
	if(a==b)
		return maxx;
	for(int i=18;i>=0;i--)
	{
		if(f[a][i]!=f[b][i])
		{
			maxx=max(maxx,max(maxn[a][i],maxn[b][i]));
			a=f[a][i],b=f[b][i];
		}
	}
	maxx=max(maxx,max(maxn[a][0],maxn[b][0]));
	return maxx;
}

signed main()
{
	n=read(),m=read();
	for(register int i=1;i<=n;i++)
		fa[i]=i;
	for(register int i=1;i<=m;i++)
	{
		e[i].u=read(),e[i].v=read(),e[i].w=read();
		e[i].id=i;
	}
	sort(e+1,e+m+1,cmp);
	for(register int i=1,num=0;i<=m;i++)//最小生成树
	{
		if(num==n-1)
			break;
		int a=find(e[i].u),b=find(e[i].v);
		if(a!=b)
		{
			fa[a]=b;
			adde(e[i].u,e[i].v,e[i].w);
			adde(e[i].v,e[i].u,e[i].w);
			ans+=e[i].w;
			flag[i]=true;
			num++;
		}
	}
	d[1]=1;
	dfs(1);
	for(register int i=1;i<=m;i++)
	{
		if(flag[i])//这条边在最小生成树
		{
			Ans[e[i].id]=ans;
			continue;
		}
		int maxx=LCA(e[i].u,e[i].v);//不在就求u、v路径上的边权最大值
		Ans[e[i].id]=ans-maxx+e[i].w;
	}
	for(register int i=1;i<=m;i++)
		printf("%lld\n",Ans[i]);
	return 0;
}

另,相似题:XSY2485,以及题解

根据提供的引用内容,你遇到的问题是在发送HTTP POST请求时收到了403 Forbidden的错误。这个错误通常表示你没有权限访问所请求的资源。 要解决这个问题,你可以采取以下步骤: 1. 首先,确保你的请求URL正确,并且你有权限访问该URL。你可以尝试在浏览器中直接访问该URL,看看是否能够成功访问。 2. 如果你确定URL是正确的,并且你有权限访问,那么可能是你的请求中缺少了必要的身份验证信息。你可以检查你的请求头中是否包含了正确的身份验证信息,比如Token或用户名密码。 3. 另外,你还可以检查服务器端的配置,确保你的请求被正确地处理和授权。你可以查看服务器的日志,以了解更多关于403错误的详细信息。 综上所述,当你收到403 Forbidden错误时,你应该首先检查URL和权限,然后确保请求中包含了正确的身份验证信息。如果问题仍然存在,你可以进一步检查服务器端的配置和日志,以找出问题的根本原因。 #### 引用[.reference_title] - *1* [kubeadm init报错10248...(The HTTP call equal to ‘curl -sSL http://localhost:10248/healthz‘ failed)](https://blog.csdn.net/weixin_45969972/article/details/123529966)[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^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c/c++使用libcurl库做http客户端及封装(HTTP_GET和HTTP_POST)](https://blog.csdn.net/xsy29000/article/details/103181267)[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^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值