"Algorithm Competition·Quick 300 Questions" One question per day: "Simplifying the Farm"

" Algorithm Competition: 300 Quick Questions " will be published in 2024 and is an auxiliary exercise book for "Algorithm Competition" .
All questions are placed in the self-built OJ New Online Judge .
Codes are given in three languages: C/C++, Java, and Python. The topics are mainly mid- to low-level topics and are suitable for entry-level and advanced students.


" Simplifying Farm ", link: http://oj.ecustacm.cn/problem.php?id=1879

Question description

[Problem description] John's farm can be regarded as a graph. The farmland represents the vertices in the graph, and the field roads represent the edges in the graph. Each edge has a certain length.
   John discovered that there were up to three paths of the same length on his farm.
   John wants to remove some of the paths so that the farm becomes a tree, so that there is only one path between the two fields.
   John wants to design the farm as a minimum spanning tree, that is, the total length of the farm roads is the shortest.
   Please help John find the total length of the minimum spanning tree, and please calculate how many types of minimum spanning trees there are in total.
[Input format] The first line of input contains two positive integers N and M, representing the number of points and sides (1 <= N <= 40,000; 1 <= M <= 100,000).
   The next M lines have three integers ai, bi, ci in each line, indicating that there is an undirected edge of length ci between points ai and bi. (1 <= ai, bi <= N; 1 <= ci <= 1,000,000)
[Output format] The output line contains two integers, representing the length of the minimum spanning tree and the number of the minimum spanning tree respectively.
Find the remainder of the number 1,000,000,007.
[Input example]

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

【Output sample】

4 3

answer

   There are two minimum spanning tree (MST) algorithms: kruskal and prim. Kruskal's idea is to be greedy for edges, "the shortest edge must be on MST"; prim's idea is to be greedy for points, "the nearest neighbor point must be on MST".
   The special points in the description of this question are: (1) There are at most three paths (edges) with the same length; (2) Calculate how many types of minimum spanning trees there are in total. The focus is on the edges, so Kruskal's algorithm is used.
   The execution steps of the Kruskal algorithm are: (1) Sort the edges; (2) Starting from the shortest edge, add the edges to the MST in order from small to large; (3) In the process of adding edges, use union lookup to determine whether a cycle is generated. If a cycle is formed, discard this edge; (4) End when all edges are processed, or end when the number of added edges is equal to n-1.
   If all edge lengths are different, then there is only one minimum spanning tree. The question states that "there are at most three sides with the same length." From the example, we can see that there are two sides of the same length, and there are also three sides of the same length. When you sort edges, these equal edges will be next to each other.
   To deal with equal-length edges, let cnt be the number of legal edges (the so-called legal means that this edge is added to MST without causing a cycle), and num is how many of these equal-length edges can be added to MST at the same time. sum is the number of minimum spanning trees.
   (1) There are two sides of equal length.
   If cnt=1, only one edge is legal, that is to say, this edge has no choice, then sum remains unchanged.
   If cnt=2, there are 2 legal edges, let’s continue the discussion:
   1) num=1, that is, only one of these two equal-length edges can be added to MST. Then sum = sum*cnt, that is, sum=sum*2. Take the following figure as an example. s1 and s2 are two subtrees that have been added to MST. There are no circles inside them. Now add two sides (x1, y1) and (x2, y2) of equal length. It is legal to add them to MST individually, but adding them at the same time will form a circle.
Insert image description here
   2) num=2, that is, these two sides of equal length should be added to MST. Then sum remains unchanged, that is, sum=sum*1. The following figure is an example.
Insert image description here
   (2) There are three sides of equal length.
   If cnt=1, only one edge is legal and sum remains unchanged.
   If cnt=2, there are two legal sides, which is the same as (1) when there are two sides of equal length and cnt=2.
   If cnt=3, there are three legal edges, then:
   1) num = 1, only one edge can be added to MST, sum = sum*cnt=sum*3. In the figure below, for example, there are three situations where you can choose any one of the three edges.
Insert image description here
   2) num = 2, there are two edges that can be added to MST, and one of the edges must be added, sum = sum*2. For example, in the figure below, you can choose two of the three edges, and there are two situations.
Insert image description here
   3) num = 2, there are two edges that can be added to MST, and they are any two edges, sum = sum*3. In the figure below, for example, you can choose two of the three edges, and there are three situations.
Insert image description here
   3) num = 3, all three edges should be added to MST, and sum remains unchanged.

【Key Points】 kruskal.

C++ code

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
const int mod = 1e9+7;
struct Node{
    
    int x,y,val;}e[N<<1];
bool cmp(Node a,Node b){
    
    return a.val<b.val;}//按边权排序
int n,m;
int s[N];          //并查集
int ans=0,sum=1;   //ans: MST的总长度, sum:最小生成树的数目
int find_set(int x){
    
                             //查询并查集,返回x的根
    if(x != s[x]) s[x] = find_set(s[x]);     //路径压缩
    return s[x];
}
void kruscal(){
    
    
	for(int i=1;i<=n;i++) s[i] = i;  //并查集初始化
	sort(e+1,e+m+1,cmp);             //边:升序排序
	for(int i=1;i<=m;){
    
                  //遍历所有边,每次处理其中的等长边
		int cnt = 0;                 //这次的等长边中,有几个可以加入MST
		set<pair<int,int> >st;       //set用于存储并去重
		int j;                       //第i~j个边等长
		for(j=i;j<=i+2 && e[i].val==e[j].val;j++){
    
     //枚举等长边,最多3个相同。更新j
			int s1 = find_set(e[j].x);   //边的一个端点属于哪个集?
			int s2 = find_set(e[j].y);   //边的另一个端点属于哪个集?
			if(s1 > s2)  swap(s1,s2);
			if(s1 != s2){
    
                    //两个集不等,这个边可以加入到MST中
				cnt ++;                  //cnt: 允许加入MST的边的数量
				st.insert(make_pair(s1,s2));   //这个边的两端点所属的集存到st中
			}
		}                                //第i~j个边是等长的
		int num = 0;
		for(;i<j;i++){
    
                       //开始时第i~j个边是等长的。i=j时退出
			int s1 = find_set(e[i].x);
			int s2 = find_set(e[i].y);
			if(s1 != s2){
    
                    //不属于一个集,可以加入到树里
				s[s2] = s1;              //并查集合并
				num++;                   //这几个等长边有num个可以同时加入树
			}
		}
		ans += e[i-1].val*num;    //这几个等长边最后有num个可以加入到MST,计算MST总长
		if(num == 1)  sum = sum*cnt%mod;   //只有一条边能加入树,直接乘以cnt
		if(cnt == 3 && num==2 && st.size() == 2) sum = 2*sum%mod;
		if(cnt == 3 && num==2 && st.size() == 3) sum = 3*sum%mod;
		 //其他情况方案数都不变
	}
}
signed main(){
    
    
	scanf("%lld%lld",&n,&m);  //读点,边
	for(int i=1;i<=m;i++)     //存边,用最简单的“边集数组”存边
		scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].val);
	kruscal();                //最小生成树
	printf("%lld %lld\n",ans,sum);
}

Java code

 

Python code

  

 

Guess you like

Origin blog.csdn.net/weixin_43914593/article/details/132629709