Codeforces 609E LCA倍增

Codeforces 609E
题目链接:
http://codeforces.com/contest/609/problem/E
题意:
给n个点(<=2e5),m条边(n-1 <= <= 2e5),问对于每条边,做一个最小生成树树的权值。
思路:
增量最小生成树,就是先构造一个最小生成树。然后对每条边找所在环上最大的边删去。
实现用LCA实现。

关于LCA,刚开始用了RMQ来做,怎么做怎么过不了样例。后来发现RMQ实现LCA的原理,是每次返回深度小的点。也就是它只能单纯的查找LCA,而不能在LCA上附带什么信息。
然后学了下LCA倍增法。
Pa[i][j]表示i的网上第2^j个祖先。假设携带信息的数组为MaxEdge[i][j],
初始化:
初始化pa数组,pa[i][j] = pa[pa[i][j-1][j-1],MaxEdge[i][j] = MAX{MaxEdge[i][j-1], MaxEdge[fa[i][j-1][j-1]}。
查找
现在要查找u和v的到他们的公共祖先上的最大边的权值
1.把u和v通过类似快速幂的做法上升至同一平面
a)假设u为深度大的点
b)For(int i = MaxLogOfDepth ; i >= 0 ; i–)
c) If(pa[u][i] != -1 && deep[pa[u][i]] >= deep[v]) 更新Ans,u = pa[u][i]
2.判断u和v是否相等
a)u和v同步上升至距离它们LCA深度为1的点处
b)For(int i = MaxLogOfDepth ; i >= 0 ; i–)
c) If(pa[u][i] != pa[v][i] ) 更新Ans,u = pa[u][i],v = pa[v][i]
3.更新Ans:Ans = Max{Ans, edge[u][0], edge[v][0]}
4.返回
源码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <utility>
using namespace std;
typedef pair<int,int> pii;
const int MAXN = 200000 + 5;
int fa[MAXN][25], dep[MAXN], MaxEdge[MAXN][20];
int pa[MAXN];
vector<pii>lin[MAXN];
struct E
{
 int u, v, w;
 int num, vis;
}e[MAXN];
bool cmp(E a, E b){return a.w < b.w;} bool cmp2(E a, E b){return a.num < b.num;} int n, m; int find_pa(int u){return u == pa[u] ? u : pa[u] = find_pa(pa[u]);} void combine(int u, int v){pa[find_pa(u)] = pa[find_pa(v)];} void dfs(int u, int p) { dep[u] = dep[p] + 1; for(int i = 0 ; i < (int)lin[u].size() ; i++){ int v = lin[u][i].first; if(v == p) continue; dfs(v, u); fa[v][0] = u; MaxEdge[v][0] = lin[u][i].second; } } void LCA_init() { for(int i = 1 ; (1 << i) < n ; i++){ for(int j = 1 ; j <= n ; j++){ if(fa[j][i - 1] == -1) continue; fa[j][i] = fa[fa[j][i - 1]][i - 1]; MaxEdge[j][i] = max(MaxEdge[j][i - 1], MaxEdge[fa[j][i - 1]][i - 1]); } } } int query(int u, int v) { if(dep[u] < dep[v]) swap(u, v); // int l = dep[u] - dep[v]; int ans = 0; // printf("u = %d, v = %d\n", u, v); int temp = u; // printf("fa[u][0] = %d, fa[u][1] = %d\n", fa[u][0], fa[u][1]); for(int j = 24 ; j >= 0 ; j--){
 if(fa[u][j] != -1 && dep[fa[u][j]] >= dep[v]) ans = max(ans, MaxEdge[u][j]), u = fa[u][j];
 }
//    printf("u = %d, v = %d\n", u, v);
//    printf("for u = 2 fa[u][0] = %d, fa[u][1] = %d, MaxEdge[u][0] = %d\n", fa[2][0], fa[2][1], MaxEdge[u][0]);
//    printf("for u = 3 fa[u][0] = %d, fa[u][1] = %d, MaxEdge[v][0] = %d\n", fa[3][0], fa[3][1], MaxEdge[v][0]);
 if(u == v) return ans;
 for(int j = 24 ; j >= 0 ; j--){
 if(fa[u][j] != fa[v][j]){
 ans = max(ans, MaxEdge[u][j]); u = fa[u][j];
 ans = max(ans, MaxEdge[v][j]); v = fa[v][j];
 }
 }
 ans = max(ans, MaxEdge[u][0]);
 ans = max(ans, MaxEdge[v][0]);
 return ans;
}
int main()
{
 while(scanf("%d%d", &n, &m) != EOF){
 for(int i = 1 ; i <= n ; i++) pa[i] = i, lin[i].clear();
 for(int i = 0 ; i < m ; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w), e[i].num = i, e[i].vis = 0;
 sort(e, e + m, cmp);
 int num = 0;
 long long ans = 0;
//        printf("first\n");
 for(int i = 0 ; i < m ; i++){
 if(find_pa(e[i].u) == find_pa(e[i].v)) continue;
 combine(e[i].u, e[i].v);
 num++;
 lin[e[i].u].push_back(make_pair(e[i].v, e[i].w));
 lin[e[i].v].push_back(make_pair(e[i].u, e[i].w));
 e[i].vis = 1;
 ans += e[i].w;
//            if(num == n - 1)    break;
 }
 memset(fa, -1, sizeof(fa));
 dep[0] = 0;
//        printf("second\n");
 dfs(1, 0);
//        printf("third\n");
//        system("pause");
 LCA_init();
 sort(e, e + m, cmp2);
 for(int i = 0 ; i < m ; i++){
//            printf("e[i].vis = %d\n", e[i].vis);
//            if(e[i].vis)    printf("%I64d\n", ans);
//            else{
 printf("%I64d\n", ans - query(e[i].u, e[i].v) + e[i].w);
//            }
 }
 }
 return 0;
}

你可能感兴趣的:(Codeforces 609E LCA倍增)