题意
- 就是给你一个有权无向图,无自环、无重边,问你,要求对每一个边i,必须在生成树里时的最小生成树的权值。
思路
- 这题思路不算太难想到,主要是第一次写树链剖分各种卡。。。这叫一个烦。。幸亏CF上可以看数据,要不然不知道调哪辈子去了。。。
- 不瞎扯了,这题就是先跑一个最小生成树,得到权值A。然后遍历每个边,对于边{u,v},如果它本身就在最小生成树中,值就是A。
- 如果不在树里,那么我们就需要看在树中的链{u,v},找出其中最大权值的边,把它删去,再加上边{u,v}即可。
- 问题就在于如何快速得到一个链上的最大权值边。想到如果不是在树上,而是一个数组中,那么我们就可以用RMQ问题的求解方法直接得到,因为没有修改,只有查询,所以st算法就可以实现。
- 那么我们现在的目标,就是把树上的边,映射到一个数组中(这就是树链剖分),然后通过rmq求解,但是映射的不好,复杂度是降不下来的,所以用了已有的重链的算法求解(刚学的。。。),具体算法这里就不细说了,以后有时间会单开文章细说。。
- 有几个麻烦点,这里说一句,一个是最小生成树边不太好定向,所以我用的方法是加双方向的边,然后用mark保证不重搜父节点。。。所以这里边就加倍了!注意maxn开的大小!
实现
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
typedef long long ll;
#define pb push_back
int idmark[maxn];
const int inf = 0x3f3f3f3f;
struct Edge{
int u,v,w,id;
void set(int uu,int vv,int ww,int idd){
u = uu;
v = vv;
w = ww;
id = idd;
}
};
struct Graph{
int n,m;
vector<int> g[maxn];
Edge edge[maxn<<1];
}gra,tree;
bool operator<(Edge e1,Edge e2){
return e1.w < e2.w;
}
int father[maxn];
int find(int x){
if (father[x] == x)
return x;
return father[x] = find(father[x]);
}
void merge(int x,int y){
x = find(x),y = find(y);
if (x == y)
return;
father[x] = y;
return;
}
void add_node(int u,int v,int w){
tree.edge[tree.m].set(u,v,w,tree.m);
tree.g[u].pb(tree.m);
tree.m++;
}
ll a[maxn];
int tot = 0;
int Max[maxn][20];
void makermq(){
for (int i = 1;i <= tot;i ++) Max[i][0] = i;
for (int i = 1;(1 << i) <= tot;i ++){
for(int j = 1;j + (1 << i) - 1 <= tot;j ++){
int p = Max[j][i - 1],q = Max[j + (1 << i - 1)][i - 1];
if (a[p] > a[q] || (a[p] == a[q] && p < q)) Max[j][i] = p;
else Max[j][i] = q;
}
}
}
int ask(int l,int r){
int tmp = int(log(r - l + 1) / log(2));
int k1,k2;
k1 = Max[l][tmp],k2 = Max[r - (1 << tmp) + 1][tmp];
if (a[k1] > a[k2] || (a[k1] == a[k2] && k1 < k2)) return k1;
else return k2;
}
int fa[maxn],dep[maxn],sz[maxn],w[maxn],son[maxn],top[maxn];
int root;
int mark[maxn];
void dfs_1(int u,int d){
dep[u] = d;
sz[u] = 1;
int maxs = 0;
for (int e=0;e<tree.g[u].size();e++){
int v = tree.edge[tree.g[u][e]].v;
if (mark[v] == 1){
continue;
}
mark[v] = 1;
fa[v] = u;
dfs_1(v,d+1);
sz[u] += sz[v];
if (maxs < sz[v]){
son[u] = v;
maxs = sz[v];
}
}
}
void dfs_2(int u){
if (sz[u] == 1)
return;
w[son[u]] = tot+1;
tot++;
top[son[u]] = top[u];
mark[son[u]] = 1;
dfs_2(son[u]);
for (int e=0;e<tree.g[u].size();e++){
int v = tree.edge[tree.g[u][e]].v;
if(v == son[u]){
a[w[v]] = tree.edge[tree.g[u][e]].w;
continue;
}
if (mark[v] == 1){
continue;
}
mark[v] = 1;
top[v] = v;
w[v] = tot+1;
tot++;
a[w[v]] = tree.edge[tree.g[u][e]].w;
dfs_2(v);
}
}
ll query(int u,int v){
int ru = top[u],rv = top[v];
ll tmp = 0;
while (ru != rv){
if (dep[ru] < dep[rv]){
swap(ru,rv);
swap(u,v);
}
tmp = max(tmp,a[ask(w[ru],w[u])]);
u = fa[ru];
ru = top[u];
}
if (u == v)
return tmp;
if (dep[u] < dep[v])
swap(u,v);
return max(tmp,a[ask(w[son[v]],w[u])]);
}
ll ans[maxn];
int main(){
cin>>gra.n>>gra.m;
for (int i=1;i<=gra.n;i++)
father[i] = i;
ll min_init = 0;
tree.n = gra.n;
tree.m = 0;
for (int i=0;i<gra.m;i++){
scanf("%d%d%d",&gra.edge[i].u,&gra.edge[i].v,&gra.edge[i].w);
gra.edge[i].id = i;
}
sort(gra.edge,gra.edge+gra.m);
root = gra.edge[0].u;
for (int i=0;i<gra.m;i++){
int u = gra.edge[i].u;
int v = gra.edge[i].v;
int w = gra.edge[i].w;
if (find(u) == find(v))
continue;
min_init += (ll)w;
add_node(u,v,w);
add_node(v,u,w);
idmark[i] = 1;
merge(u,v);
}
mark[root] = 1;
dfs_1(root,1);
top[root] = root;
memset(mark,0,sizeof(mark));
mark[root] = 1;
dfs_2(root);
makermq();
for (int i=0;i<gra.m;i++){
int id = gra.edge[i].id;
if (idmark[i] == 1)
{
ans[id] = min_init;
continue;
}
int u = gra.edge[i].u;
int v = gra.edge[i].v;
int w = gra.edge[i].w;
ans[id] = min_init + (ll)w - (ll)query(u,v);
}
for (int i=0;i<gra.m;i++){
printf("%I64d\n",ans[i]);
}
return 0;
}