CodeForces 609E Minimum spanning tree for each edge (lca+最小生成树+倍增)

题意:给出一个n个点m条边的无向图,问每条边所在的最小生成树的权值和是多少。

思路:首先求出整个图的最小生成树,然后对于任意一条边替换这条边两点到lca的最大边,这个值就是这条边所在的最小生成树的权值和,可以用倍增记录一下每个点的祖先和到这个祖先的最大边,然后处理出lca,对于每条边求出答案。

<pre class="cpp" name="code">#include <bits/stdc++.h>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#define pb push_back
#define mp make_pair
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int MAXN = 200100;
int n, m;
struct Edge {
    int to, next;
    int w;
} edge[MAXN*2];
int head[MAXN], tot;
void addedge(int u,int v,int w) {
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
struct Query
{
    int q,next;
    int index;//查询编号
} query[MAXN*2];
int h[MAXN];
int tt;
void add_query(int u,int v,int index)
{
    query[tt].q = v;
    query[tt].next = h[u];
    query[tt].index = index;
    h[u] = tt++;
    query[tt].q = u;
    query[tt].next = h[v];
    query[tt].index = index;
    h[v] = tt++;
}

void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
    tt = 0;
    memset(h,-1,sizeof(h));
}
//倍增
int fa[MAXN][20], max_edge[MAXN][20], dep[MAXN];
void init_fa(int u, int p, int c) {
	dep[u] = dep[p] + 1;
	fa[u][0] = p;
	max_edge[u][0] = c;
	for (int i = 1; fa[u][i-1]; i++) {
		fa[u][i] = fa[ fa[u][i-1] ][i-1];
		max_edge[u][i] = max(max_edge[u][i-1], max_edge[ fa[u][i-1] ][i-1]);
	}
}

int cal(int u, int lca) {
	int d = dep[u] - dep[lca];
	int ans = 0;
	for(int i = 18; i >= 0; i--) {
		if ((1<<i) <= d) {
			d -= (1<<i);
			ans = max(ans, max_edge[u][i]);
			u = fa[u][i];
		}
	}
	return ans;
}

//lca
int pnt[MAXN], ans[MAXN];
bool vis[MAXN];
int find(int x) {
	if(x == pnt[x]) return x;
	return pnt[x] = find(pnt[x]);
}
void dfs(int u) {
	vis[u] = 1; 
	pnt[u] = u;
	for(int i = head[u]; i != -1; i = edge[i].next) {
		int v = edge[i].to;
		if(vis[v]) continue;
		init_fa(v, u, edge[i].w);
		dfs(v);
		pnt[v] = u;
	}
	for(int i = h[u]; i != -1; i = query[i].next) {
		int v = query[i].q;
		if(vis[v]) ans[query[i].index] = find(v);
	}
}

//最小生成树
int u[MAXN], v[MAXN], w[MAXN], p[MAXN], r[MAXN];
int find_p(int x) {
	return x == p[x] ? x : p[x]=find_p(p[x]); 
}
bool cmp(const int x, const int y) {
	return w[x] < w[y];
}
LL kruscal() {
	LL ans = 0;
	for (int i = 1; i <= n; i++) p[i] = i;
	for (int i = 1; i <= m; i++) r[i] = i;
	sort(r+1, r+m+1, cmp);
	for (int i = 1; i <= m; i++) {
		int e = r[i];
		int x = find_p(u[e]), y = find_p(v[e]);
		if (x != y) {
			p[x] = y;
			ans += w[e];
			addedge(u[e], v[e], w[e]);
			addedge(v[e], u[e], w[e]);
		}
	}
	return ans;
}
int main()
{
	freopen("input.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	init();
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &u[i], &v[i], &w[i]);
		add_query(u[i], v[i], i);
	}
	LL tmp = kruscal();
	dfs(1);
	for (int i = 1; i <= m; i++) 
		printf("%I64d\n", tmp+w[i]-max(cal(u[i], ans[i]), cal(v[i], ans[i])));
	return 0;
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值