传送门
题解:
首先由贪心策略,我们知道对于一条树边,我们不可能加它的权值,对于一条非树边,我们不可能减少它的权值。
而一条非树边对应树上一条路径,显然如果我们要求这棵树是某一棵最小生成树,则修改后这条非树边的权值必须大于等于这条路径上任何一条边的权值。
设 T T T表示树边集合, E E E表示非树边集合, ∀ e ∈ E \forall e\in E ∀e∈E, P e P_e Pe表示 e e e的两个端点的树上路径的边的集合。设 w e w_e we表示这条边的原权值, d e d_e de表示变化量,然后稍微考虑一下是一个线性规划:
l i m i t s : ∀ e ∈ E , ∀ t ∈ P e , w e + d e ≥ w t − d t 即 d e + d t ≥ w t − w e m i n i m i z e : ∑ e d e \begin{aligned} limits:&&&&&\forall e\in E,\forall t\in P_e,w_e+d_e\geq w_t-d_t\\ &&&&&即d_e+d_t\geq w_t-w_e\\ minimize:&&&&&\sum_{e} d_e \end{aligned} limits:minimize:∀e∈E,∀t∈Pe,we+de≥wt−dt即de+dt≥wt−wee∑de
如果要闲得蛋疼写单纯性也行,不过这个形式就是KM的最小顶标和。。。
注意一下,KM算法是从 X X X部对 Y Y Y部进行增广,我们可能需要对 Y Y Y部进行扩大,使得存在 X X X部对 Y Y Y部的完备匹配,
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
typedef std::pair<int,int> pii;
#define fi first
#define se second
cs int INF=0x3f3f3f3f;
namespace KM{
cs int N=757;
int a[N][N],nx,ny,n;
int wx[N],wy[N];
int vs[N],cy[N],idx;
int slk[N],pre[N];
inline void solve(int x){
cy[0]=x;int y=0,delta,to;
do{
vs[y]=idx,x=cy[y],delta=INF;
for(int re j=1;j<=n;++j)
if(vs[j]!=idx){
int t=wx[x]+wy[j]-a[x][j];
if(t<slk[j])slk[j]=t,pre[j]=y;
if(slk[j]<delta)delta=slk[j],to=j;
}
for(int re j=0;j<=n;++j)
if(vs[j]==idx)wx[cy[j]]-=delta,wy[j]+=delta;
else slk[j]-=delta;
y=to;
}while(cy[y]);
do{
cy[y]=cy[pre[y]];
y=pre[y];
}while(y);
}
inline int work(){
n=std::max(nx,ny);
for(int re i=1;i<=nx;++i)
for(int re j=1;j<=ny;++j)wx[i]=std::max(wx[i],a[i][j]);
for(int re i=1;i<=nx;++i){
memset(slk+1,0x3f,sizeof(int)*n);
++idx;solve(i);
}int ans=0;
for(int re i=1;i<=nx;++i)ans+=wx[i];
for(int re i=1;i<=ny;++i)ans+=wy[i];
return ans;
}
}
using KM::a;
using KM::nx;
using KM::ny;
namespace Graph{
cs int N=55;
int n,m;
bool tree_edge[N][N];
int w[N][N],d[N],fa[N],id_p[N],val[N];
pii E[N*N];std::vector<int> G[N];
inline void dfs(int u,int p){
fa[u]=p;d[u]=d[p]+1;
if(p)id_p[u]=++nx,val[u]=w[u][p];
for(int re v:G[u])if(p!=v)dfs(v,u);
}
inline void get_cir(int u,int v,int w){
while(u^v){
if(d[u]>d[v])std::swap(u,v);
if(val[v]>w)a[id_p[v]][ny]=val[v]-w;
v=fa[v];
}
}
inline void work(){
n=gi(),m=gi();
for(int re i=1;i<=m;++i){
int u=gi(),v=gi();
E[i]=pii(u,v);
w[u][v]=w[v][u]=gi();
}
for(int re i=1;i<n;++i){
int u=gi(),v=gi();
tree_edge[u][v]=tree_edge[v][u]=true;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
for(int re i=1;i<=m;++i){
int u=E[i].fi,v=E[i].se;
if(!tree_edge[u][v]){
++ny;
get_cir(u,v,w[u][v]);
}
}
}
}
signed main(){
#ifdef zxyoi
freopen("mst.in","r",stdin);
#endif
Graph::work();
cout<<KM::work()<<"\n";
return 0;
}