F. MST Unification
题目链接:http://codeforces.com/contest/1108/problem/F 题意:给你n个点(1<=n<=2e5),m条边(n-1 <= m <= 2e5),每个边包含两个端点和权值,你唯一能进行的操作是可以将任意边的权值加一(可多次叠加),使得其能够组成最小生成树的边集只有一种。问最少需要进行几次操作 思路:
Kruskal 算法 是现将边按权值从小到大排序,从权值小的边开始选择,如果选择的边和已经连起来的边不会形成环,那么就将这条边连起来。 也就是说唯一可以形成多个最小生成树的是一个由相同权值的边构成的连通块里形成环的那些多余的边。这些边连不连通对后续的选择没有影响。(权值比该连通块里的边的 权值大的那些边会被kruskal过滤掉) 只要将这些边的权值加一,即能使无法形成最小生成树,因为他在一个由比它更小的边形成的连通块中。
#include <bits/stdc++.h>
#define pi acos(-1.0 )
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long LL;
typedef pair< int , int > PII;
const int INF = 0x3f3f3f3f ;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f ;
const int maxn = 2e5 + 100 ;
const LL mod = 1e9 + 7 ;
struct Edge
{
int u, v, w;
bool operator< ( const Edge & b) const
{
return w< b. w;
}
} es[ maxn] ;
int par[ maxn] ;
int rankt[ maxn] ;
void init ( int n)
{
for ( int i= 1 ; i<= n; i++ ) {
par[ i] = i;
rankt[ i] = 0 ;
}
}
int findr ( int x)
{
if ( par[ x] == x) return x;
else
return findr ( par[ x] ) ;
}
void unite ( int x, int y)
{
x = findr ( x) ;
y = findr ( y) ;
if ( x == y) return ;
if ( rankt[ x] < rankt[ y] )
par[ x] = y;
else {
par[ y] = x;
if ( par[ y] == par[ x] ) rankt[ x] ++ ;
}
}
bool same ( int x, int y)
{
return findr ( x) == findr ( y) ;
}
int main ( )
{
init ( 2e5 ) ;
int n, m;
scanf ( "%d%d" , & n, & m) ;
for ( int i= 1 ; i<= m; i++ ) {
scanf ( "%d%d%d" , & es[ i] . u, & es[ i] . v, & es[ i] . w) ;
}
sort ( es+ 1 , es+ 1 + m) ;
int ans= 0 ;
int L= 0 ;
for ( int i= 1 ; i<= m; i++ ) {
if ( ! same ( es[ i] . u, es[ i] . v) ) ans++ ;
if ( i+ 1 <= m && es[ i] . w< es[ i+ 1 ] . w) {
for ( ; L<= i; L++ ) {
if ( ! same ( es[ L] . u, es[ L] . v) ) unite ( es[ L] . u, es[ L] . v) ;
}
}
}
printf ( "%d\n" , ans - n + 1 ) ;
}