CF1108F MST Unification

CF1108F MST Unification

前言

神说,你需要一颗最小生成树,于是你求出了最小生成树。

神又说,你这生成树不唯一啊,快去改改,于是就有了这篇题解

思路

P4180 [BJWC2010] 严格次小生成树 ,先算出最小生成树。

由于要保证树唯一,考虑怎么才可以让其唯一。

回顾 克鲁斯卡尔 算法可以发现,如果有相同长度的边,连接的两个相同的点集,那么这两个边都可选,这就是最小生成树多解的原因。

于是就有了两种方式:

方一

引用自博客 大佬:良心WA题人

我们考虑将长度相同的边一起处理,具体如下:

  1. 先将长度相同的边给找出来
  2. 记录在没有连接这些边的情况下,边两端点集不同的边的边数为 \(cnt\)
  3. 从前到后连边,如果可以连接则 cnt--

最后 \(ans=\sum cnt\)

方二

直接考虑生成树后树上倍增,对于一条非树边,求得其两端点之间的简单路径上边权的最大值,若与该非树边边权相等,则 ans++

CODE
// #pragma GCC optimize("Ofast")
// #pragma GCC optimize("inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
typedef long double ld;
const ll maxn=2e5+2;
 
inline ll read_int(){
	ll a=0;bool f=0;char g=getchar();
	while(g<'0'||g>'9') {if(g=='-') f=1;g=getchar();}
	while('0'<=g&&g<='9') a=a*10+g-'0',g=getchar();
	return f ? -a : a;
}
 
inline void write(ll a,bool f=1){
	char lin[40];ll top=0;
	if(a<0) a=-a,putchar('-');
	while(a) lin[++top]=a%10+'0',a/=10;
	if(!top) lin[++top]='0';
	while(top) putchar(lin[top--]);
	if(f) putchar('\n');
}
 
struct SJ{int f,t,v;}sj[maxn];int cnt;
bool operator < (const SJ a,const SJ b){return a.v<b.v;}
 
struct E{
    int t,n;
    ll v;
}edge[maxn*2];
int l,head[maxn];
 
struct BCJ{
    int bcj[maxn];
    inline void neww(int n){
        for(int i=1;i<=n;i++) bcj[i]=i;
    }
    inline int find(int s){return (s==bcj[s] ? s : (bcj[s]=find(bcj[s])));}
    inline void add(int a,int  b){
        bcj[find(a)]=find(b);
    }
    inline bool link(int a,int b){return find(a)==find(b);}
}bcj;
 
int n,m;
 
ll bz[maxn][21],point[maxn][21],dep[maxn];
inline void dfs(int s,int f,int W){
    bz[s][0]=W,point[s][0]=f;
    dep[s]=dep[f]+1;
    for(int i=1;i<=20;i++){
        bz[s][i]=max(bz[s][i-1],bz[point[s][i-1]][i-1]);
        point[s][i]=point[point[s][i-1]][i-1];
    }
    for(int i=head[s];i;i=edge[i].n){
        int t=edge[i].t;
        if(t==f) continue;
        dfs(t,s,edge[i].v);
    }
}
 
inline int upto(ll &ans,int s,int C){
    for(int i=20;~i;i--){
        if((1<<i)&C) ans=max(ans,bz[s][i]),s=point[s][i];
    }
    return s;
}
 
inline int LCA_max(int a,int b){
    ll ans=0;
    if(dep[a]>dep[b]) swap(a,b);
    b=upto(ans,b,dep[b]-dep[a]);
    if(a==b) return ans;
    for(int i=20;~i;i--){
        if(point[a][i]==point[b][i]) continue;
        ans=max(ans,max(bz[a][i],bz[b][i]));
        a=point[a][i],b=point[b][i];
    }
    return max(ans,max(bz[a][0],bz[b][0]));
}
 
inline void read(){
    n=read_int(),m=read_int();
    for(int i=1;i<=m;i++){
        sj[i].f=read_int(),sj[i].t=read_int(),sj[i].v=read_int();
        if(sj[i].f==sj[i].t){i--,m--;}
    }
    bcj.neww(n);
    sort(sj+1,sj+1+m);
    for(int i=1;i<=m;i++){
        if(bcj.link(sj[i].f,sj[i].t)){sj[++cnt]=sj[i];continue;}
        l++,edge[l]=(E){sj[i].t,head[sj[i].f],sj[i].v},head[sj[i].f]=l;
        l++,edge[l]=(E){sj[i].f,head[sj[i].t],sj[i].v},head[sj[i].t]=l;
        bcj.add(sj[i].f,sj[i].t);
    }
    dfs(1,1,0);
    int ans=0;
    for(int i=1;i<=cnt;i++){
        ans+=(LCA_max(sj[i].f,sj[i].t)==sj[i].v);
    }
    write(ans);
}
 
int main (){
    // freopen(".in","r",stdin);
    read();
    // while(1) getchar();
}

posted @ 2022-11-16 11:15  轩Demonmaster  阅读(7)  评论(0编辑  收藏  举报