太好了,一晚上写了一道题,感觉至少是紫题,一看只是 *2300,甚至洛谷上评的蓝……但这不妨碍是一道很好的题。

首先,很明显如果存在欧拉回路的话就是直接把每一条边都走一遍就好了。如果不是的话,那必然有偶数个奇度点($a_1+2a_2=2m$,所以 $2\mid a_1$)。

那我们考虑在每条边都走的基础上,在奇度点使用操作 2 就好了。我们可以把一次传送看作加一条边,一次传送会让那两个点的度数都 $+1$ 变为偶度点,这样既可以让原图变成欧拉图,还刚好不会多加度数。

那传送代价怎么弄最小呢?观察题目,权值是路径上最大编号的边的边权,最小化最大边权,不就是最小瓶颈路吗?以编号为序使用 kruskal 重构树即可(一道板子题)。重构树上的 LCA 即是路径权值。
由于这里是编号来排序,边权未必最小,我们考虑如果已经当前这条边的两端已经联通的情况。目前枚举到的编号一定大于之前的,所以如果使用这条边的话连通块的权值一定变为这条边的边权,如果权值更小那我们就采用就好了。路径的权值更新在他们的 LCA 上。

最后的计算部分,类似树形 DP。dfs 时遍历到的点作为下面点的 LCA,如果一颗子树下面的奇度点数是奇数(即还有未变成偶度点的),那就让它和另一颗子树中剩余的奇度点匹配即可。传送的最小代价是 LCA 到根中的最小权值(因为重构树父节点编号一定比子节点大,值小可以取)。令 $a_v$ 表示以 $v$ 为根的子树下是否有剩余的奇度点,进行 $\lfloor \frac{\sum a_v}{2}\rfloor$ 次传送即可,剩余的奇度点往上传。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e6+5,inf=1e9;
struct DSU{
int fa[N],sz[N];
void reset(int n){
for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1;
}
int find(int x){
return (fa[x]==x?x:fa[x]=find(fa[x]));
}
void merge(int x,int y){
x=find(x),y=find(y);
if(x==y) return;
if(sz[x]<sz[y]) swap(x,y);
fa[y]=x,sz[x]+=sz[y];
}
}mk;
int n,m,val[N],f[N],sz[N],deg[N];
vector<int> g[N];
ll ans;
void dfs(int u,int mnw){
mnw=min(mnw,val[u]);
sz[u]=deg[u]%2;
for(int v:g[u]){
dfs(v,mnw);
sz[u]+=sz[v];
}
ans+=1ll*sz[u]/2*mnw;
sz[u]%=2;
}
void solve(){
cin>>n>>m;
mk.reset(n+m);
for(int i=1;i<=n+m;i++) val[i]=inf,f[i]=i,g[i].clear(),sz[i]=deg[i]=0;
ans=0;
int tot=n;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
deg[u]++;deg[v]++;ans+=w;
u=mk.find(u),v=mk.find(v);
if(u!=v){
tot++;
g[tot].push_back(f[u]);
g[tot].push_back(f[v]);
mk.merge(u,v);
val[tot]=w;
f[mk.find(u)]=tot;
}
else val[f[u]]=min(val[f[u]],w);
}
dfs(tot,inf);
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}