题意: 一开始可以选择一条边免费的最小生成树,并求能 达到这个最小生成树的一开始选择边的方案数
首先我们可以想到答案是最小生成树去掉其中最大边权的值。
但是讨论方案数的时候,我们会发现我们并不是只让最小生成树上的所有最大边权免费,而是只要能找到一条边,而这条边可以代替树的最大边权,然后让这条边免费就行了。
如果有这么一条边存在,那么在最小生成树上加入这条边必然会有环,并且环在树上的部分一定是最小生成树上的最大边权。
因此考虑枚举每条边,由于不在最小生成树上的边加入生成树都会出现环,因此我们只需要查询这条边两个点在最小生成树中的路径中是否包含最大权值的边。
问题变成了如何查询树上两点间最大边权是否是某个值。可以树链剖分,不过也可以去掉树上的所有最大边权再做并查集,来判断两点的连通性。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
const LL inf=0x3f3f3f3f;
const LL maxn=1e5+5;
inline void _read(LL &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
LL n,m,vis[maxn<<1],maxx,ans,tot,fa[maxn],fa1[maxn];
struct Edge{
LL from,to,d;
bool operator <(const Edge& h)const{
return d<h.d;
}
}edges[maxn<<1];
LL getfa(LL x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
LL getfa1(LL x){return fa1[x]==x?x:fa1[x]=getfa1(fa1[x]);}
int main(){
_read(n);_read(m);
for(LL i=1;i<=m;i++){
_read(edges[i].from);_read(edges[i].to);_read(edges[i].d);
}
for(LL i=1;i<=n;i++)fa[i]=fa1[i]=i;
sort(edges+1,edges+1+m);
for(LL i=1;i<=m;i++){
LL u=edges[i].from,v=edges[i].to;
LL fu=getfa(u),fv=getfa(v);
if(fu!=fv)fa[fu]=fv,ans+=edges[i].d,vis[i]=1,maxx=max(maxx,edges[i].d);
}
ans-=maxx;
for(LL i=1;i<=m;i++)
if(vis[i]&&maxx!=edges[i].d){
LL u=edges[i].from,v=edges[i].to;
LL fu=getfa1(u),fv=getfa1(v);
if(fu!=fv)fa1[fu]=fv;
}
for(LL i=1;i<=m;i++){
if(vis[i]&&maxx!=edges[i].d)continue;
if(vis[i]&&maxx==edges[i].d){
tot++;
continue;
}
LL u=edges[i].from,v=edges[i].to;
LL fu=getfa1(u),fv=getfa1(v);
if(fu!=fv)tot++;
}
cout<<ans<<" "<<tot;
}