BZOJ 1977 Tree 次小生成树 (kruskal st表 倍增lca)
2017-07-10 17:13
302 查看
1977: [BeiJing2010组队]次小生成树 Tree
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3121 Solved: 791
[Submit][Status][Discuss]
Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
思路:
要求次小生成树,肯定先要求最小生成树,考虑到次小生成树肯定是最小生成树更换一条边而得(加上一条边后图中肯定会出现一个环,用这条边替换掉其余边中最大的一条),所以枚举每条不在最小生成树上的边(u, v),因为是严格次小,所以求u和v路径上的最大边权和次大边权。
如果最大边权和(u, v)的边权相等,那么减去次大边的边权,加上(u, v)的边权,更新答案。
如果最大边权比(u, v)的边权要小,那么减去最大边的边权,加上(u, v)的边权,更新答案。
用st表记录max [ x ][ i ]表示x的从x向上走2^i步所经过路径上的最大值、次大值就可以做到O(nlogn)。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define LL long long using namespace std; const int maxn=100010, maxm=300010, maxk=22; const int INF = 1e9+7; int n, m, delta, idc; int head[maxn], acc[maxn][maxk], dep[maxn], fat[maxn], fst[maxn][maxk], snd[maxn][maxk]; LL ans; bool in[maxm]; struct Edge{ int x, y, v; }ed[maxm]; struct Edge1{ int to, next, w; }ed1[maxm]; bool cmp(Edge a, Edge b){ return a.v < b.v; } void adde(int u, int v, int w){//记录x->y的路径 ed1[++idc].to = v; ed1[idc].next = head[u]; head[u] = idc; ed1[idc].w = w; } int findfa(int x){//并查集 return fat[x]==x ? x:fat[x]=findfa(fat[x]); } void dfs(int u){//初始化st表 for(int i=1; i<=20; i++){ acc[u][i] = acc[acc[u][i-1]][i-1];//维护到达位置 int no1 = fst[u][i-1], no2 = fst[acc[u][i-1]][i-1]; fst[u][i] = max(no1, no2);//维护最长边的边长 snd[u][i] = max(snd[u][i-1], snd[acc[u][i-1]][i-1]); if(no1 != no2) snd[u][i] = max(snd[u][i], min(no1, no2));//维护次长边的边长 } for(int k=head[u]; k; k=ed1[k].next){ int v = ed1[k].to; if(v != acc[u][0]){ dep[v] = dep[u] + 1; acc[v][0] = u; fst[v][0] = ed1[k].w; dfs( v ); } } } void kruskal(){ sort(ed+1, ed+1+m, cmp); int cnt = 0; for(int i=1; i<=n; i++) fat[i] = i;//初始化father for(int i=1; i<=m; i++){ if(cnt == n-1) break;//完成 int u = ed[i].x, v = ed[i].y, w = ed[i].v; if(findfa(u) == findfa(v)) continue; cnt++; fat[findfa(u)] = findfa(v); in[i] = 1;//记录它是否在最小生成树中 adde(u, v, w); adde(v, u, w);//构造最小生成树,保存两点间路径 ans += w; } } int lca(int u, int v){//倍增求lca if(dep[u] < dep[v]) swap(u, v); int h = dep[u] - dep[v]; for(int i=20; i>=0 && h; i--) if( h & (1<<i) ) u = acc[u][i];//跳到同一深度 if(u == v) return u; for(int i=20; i>=0; i--) if(acc[u][i] != acc[v][i]){ u = acc[u][i]; v = acc[v][i]; } return acc[u][0]; } void query(int u, int lc, int w){ int max1 = 0, max2 = 0; for(int i=20,h=dep[u]-dep[lc]; i>=0; i--) if( h & (1<<i) ){ if(fst[u][i] > max1){ max2 = max1; max1 = fst[u][i]; } max2 = max(max2, snd[u][i]); h -= (1<<i); } if(w == max1) delta = min(delta, w-max2); else delta = min(delta, w-max1); } void solve(int x){ int u = ed[x].x, v = ed[x].y, w = ed[x].v, lc = lca(u, v); query(u, lc, w); query(v, lc, w);//在整条链上维护最长路与新边的差的最小值(相等的话就是次小边) } int main(){ delta = INF; scanf("%d%d", &n, &m); for (int i=1; i<=m; i++){ int x, y, z; scanf("%d%d%d", &x, &y, &z); ed[i] = (Edge){x, y, z}; } kruskal(); dfs( 1 ); for(int i=1; i<=m; i++) if ( !in[i] ) solve( i ); printf("%lld\n", ans+delta); return 0; }
相关文章推荐
- 洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)
- 【BZOJ】1977 [BeiJing2010组队]次小生成树 Tree kruskal+LCA
- bzoj1977 [BeiJing2010]次小生成树 Tree(kruskal+树上倍增)
- bzoj 1977 (浅谈如何hack掉hzwer学长)(严格次小生成树)(LCA+kruskal)
- BZOJ-1977 次小生成树 Tree 树上倍增LCA+Kruskal+位运算
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- [bzoj1977][BeiJing2010组队]次小生成树 Tree——树上倍增+lca
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- CodeForces 609 E.Minimum spanning tree for each edge(最小生成树-Kruskal+在线倍增LCA)
- 【BZOJ1977】【MST】【LCA】[BeiJing2010组队]次小生成树 Tree 题解
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- bzoj 1977: [BeiJing2010组队]次小生成树 Tree 最小生成树+倍增
- BZOJ 1977([BeiJing2010组队]次小生成树 Tree-LCA的位运算)
- bzoj1977: [BeiJing2010组队]次小生成树 Tree 树上倍增
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- BZOJ 1977 次小生成树 倍增LCA
- 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree【次小生成树】【LCA】
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- bzoj1977 次小生成树【最小生成树+倍增】