#2020.02.05训练题解#最小生成树入门(F题)

题源CF-1108

CF-1108-MST Unification

Description
You are given an undirected weighted connected graph with n vertices and m edges without loops and multiple edges.
The i-th edge is ei=(ui,vi,wi); the distance between vertices ui and vi along the edge ei is wi (1≤wi). The graph is connected, i. e. for any pair of vertices, there is at least one path between them consisting only of edges of the given graph.
A minimum spanning tree (MST) in case of positive weights is a subset of the edges of a connected weighted undirected graph that connects all the vertices together and has minimum total cost among all such subsets (total cost is the sum of costs of chosen edges).
You can modify the given graph. The only operation you can perform is the following: increase the weight of some edge by 1. You can increase the weight of each edge multiple (possibly, zero) times.
Suppose that the initial MST cost is k. Your problem is to increase weights of some edges with minimum possible number of operations in such a way that the cost of MST in the obtained graph remains k, but MST is unique (it means that there is only one way to choose MST in the obtained graph).
Your problem is to calculate the minimum number of operations required to do it.

Input
The first line of the input contains two integers n and m (1≤n≤2⋅105,n−1≤m≤2⋅105) — the number of vertices and the number of edges in the initial graph.
The next m lines contain three integers each. The i-th line contains the description of the i-th edge ei. It is denoted by three integers ui,vi and wi (1≤ui,vi≤n,ui≠vi,1≤w≤109), where ui and vi are vertices connected by the i-th edge and wi is the weight of this edge.
It is guaranteed that the given graph doesn’t contain loops and multiple edges (i.e. for each i from 1 to m ui≠vi and for each unordered pair of vertices (u,v) there is at most one edge connecting this pair of vertices). It is also guaranteed that the given graph is connected.

Output
Print one integer — the minimum number of operations to unify MST of the initial graph without changing the cost of MST.

Examples
Input
8 10
1 2 1
2 3 2
2 4 5
1 4 2
6 3 3
6 1 3
3 5 2
3 7 1
4 8 1
6 2 4
Output
1
Input
4 3
2 1 3
4 3 4
2 4 1
Output
0
Input
3 3
1 2 1
2 3 2
1 3 3
Output
0
Input
3 3
1 2 1
2 3 3
1 3 3
Output
1
Input
1 0
Output
0
Input
5 6
1 2 2
2 3 1
4 5 3
2 4 2
1 4 2
1 5 3
Output
2
Note
The picture corresponding to the first example:
#2020.02.05训练题解#最小生成树入门(F题)_第1张图片
You can, for example, increase weight of the edge (1,6) or (6,3) by 1 to unify MST.
The picture corresponding to the last example:
#2020.02.05训练题解#最小生成树入门(F题)_第2张图片
You can, for example, increase weights of edges (1,5) and (2,4) by 1 to unify MST.

题意

  • 多组输入,每组先输入一个N和M,分别代表顶点数和边数
  • 接下来给出M条边连通的两个点坐标,以及连通长度
  • 题意所给的图为连通图,每条边可对边权进行+1的操作
  • 在不改变原图最小生成树边权和的前提下,进行几次操作,可以使得新图的最小生成树唯一
  • 每组数据输出对应的操作次数

题解

  • 这是最小生成树模板题,对模板进行微改即可(思维上略不同于模板的直接思维)
  • 如果最小生成树不唯一,那么图中肯定存在了多条权值相同的边
  • 进入函数前,已经对边按照权值进行了升序,所以关键是遇到边权相同的边如何处理
  • 函数内部用cnt统计要操作多少条边,容易疏忽的点在于,边权相同不一定只取其一
  • 如果边权和之前的相同,但是这条边算上也不会造成回路,那么相对于别的更长的边,为何不取这条短的呢
  • 只有边权与之前相同,如果都连通,就会造成回路时,取其一来连通
  • 同时,题意给出,所给定的图都为连通图,故如果边权相同+连通造成回路,直接操作+1舍弃这条边即可
  • 多选一的边本身就只能选其一不多选一的边依旧继续
  • 那么多选一后没被选中的边本身也用不到,所以操作后不会对后续产生影响

详见下图三个关键形态的思维点,以及代码行的注释

#2020.02.05训练题解#最小生成树入门(F题)_第3张图片

涉及知识点

  • 最小生成树MST 算法(此题用到Kruskal算法)
  • 对于最小生成树的算法-详见链接博客介绍最小生成树

AC代码

#include
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=2e5+10;
int father[maxn];
int n,m,cnt,key;
struct node
{
	int st;
	int ed;
	int w;
}edge[maxn];
bool cmp(node x,node y)
{
	return x.w<y.w;
}
void init()
{
	for(int i=1;i<=n;i++) father[i]=i;
	memset(edge,0,sizeof(edge));
}
int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}
void kruskal()
{
	int i;
	cnt=0;//统计操作次数
	for(i=1;i<=m;++i)
	{
		for(key=i;edge[i].w==edge[key].w;++key);//遍历得到从何处开始边权不同
		for(int j=i;j<key;++j)
		{
			int fx=find(edge[j].st);
			int fy=find(edge[j].ed);
			if(fx!=fy) cnt++;//连通成环则不可取,不必操作;不成环的假设操作 
		}
		for(int j=i;j<key;++j)
		{
			int fx=find(edge[j].st);
			int fy=find(edge[j].ed);
			if(fx!=fy)
			{
				father[fx]=father[fy];
				/*
					第i个位置先被遍历,满足if,其实这是重复序列的第一个
					自己和自己重复,实际是不能算是重复
					所以自己这一遍操作肯定不用,于是进行cnt--; 
				*/
				cnt--;
				/*
					按排序后的顺序一个个遍历
					排在前面且满足条件的已经连通
					现在这个edge[j].st和edge[j].ed连通后依旧不成环(非同祖先)
					全连不环都要取,别误操作 ,于是进行cnt--; 
				*/
			}
		}
		/*
			实际只有之前的连通,现在的假设连通会成环(现在两点同祖先)时 
			两种连通方式二选一,会导致多种形态的最小生成树
			但之前同边权的已经按照边权进行过连通
			这个的边权+1对整体最小值不变,因而可进行+1的操作
			题目保证给定图是连通的,即保证了“全连成环选其一”
			要操作的进行一次+1就够啦 
		*/ 
		i=key-1;//此处-1是因为在for循环内部有++i,还会加回来的 
		//从 i 到 key-1 的边权都是相等的,处理完后接下来从 key 开始处理即可 
	}
}
int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		init();
		for(int i=1;i<=m;i++) scanf("%d %d %d",&edge[i].st,&edge[i].ed,&edge[i].w);
		sort(edge+1,edge+m+1,cmp);//对边按照权值进行排序
		kruskal();
		printf("%d\n",cnt);
	}
}

你可能感兴趣的:(2020.02.05训练题解)