严格次小生成树(lca + 倍增)
题目描述
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) ∑e∈EMvalue(e)<∑e∈ESvalue(e)
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
输入输出格式
输入格式:
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
输出格式:
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
首先用kruskal做一下最小生成树,把这些边称为“树边”,其余的为”非树边“,将每一条边权为v的非树边加到生成树中,则形成一个环,设环上的最大边和次大边分别为val1,val2,则显然:
当 val1 < v 时 sum + v - val1 就是一个候选答案;
当 val1 = v 时 sum + v - val2 就是一个候选答案;
比较所有的候选答案,最小的即是该题的解。那么问题就转换成:如何求环上的最大边和次大边;
可以用lca来写,f[i][k]表示点i向上跳2的k次方所对应的点,dis1[i][k] 和 dis2[i][k] 分别表示点i向上跳2的k次方所对应的最大边和次大边,则显然:
f[i][k] = f[f[i][k - 1]][k - 1];
dis1[i][k] = max(dis1[i][k - 1],dis1[f[i][k - 1]][k - 1]);
if(dis1[i][k - 1] == dis1[f[i][k - 1]][k - 1])
dis2[i][k] = max(dis2[i][k - 1],dis2[f[i][k - 1]][k - 1]);
if(dis1[i][k - 1] > dis1[f[i][k - 1]][k - 1])
dis2[i][k] = max(dis2[i][k - 1],dis1[f[i][k - 1]][k - 1]);
if(dis1[i][k - 1] < dis1[f[i][k - 1]][k - 1])
dis2[i][k] = max(dis1[i][k - 1],dis2[f[i][k - 1]][k - 1]);
初值为:
f[i][0] = father(i); dis1[i][0] = edge.v; dis2[i][0] = -INF(不存在)
最后求一下,时间为(mlogn + mlogm);
#include <bits/stdc++.h> using namespace std; #define ll long long #define INF 0x3f3f3f3f #define MAXN 1000010 #define MAXM 100010 inline int read() { int x = 0,ff = 1; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * ff; } ll sum = 0; int n,m,tem = INF,tot = 0,cnt = 0; int vis[MAXN],fa[MAXN],lin[MAXN],deep[MAXN],f[MAXM][25],dis1[MAXM][25],dis2[MAXM][25]; struct tree { int x,y,v; } t[MAXN]; struct edge { int y,v,next; } e[MAXN]; inline bool cmp(tree x,tree y) { return x.v < y.v; } inline int get(int x) { return fa[x] == x ? x : (fa[x] = get(fa[x])); } inline void add(int xx,int yy,int vv) { e[++tot].y = yy; e[tot].v = vv; e[tot].next = lin[xx]; lin[xx] = tot; } void Kruskal() { sort(t + 1,t + m + 1,cmp); for(int i = 1; i <= n; ++i) fa[i] = i; for(int i = 1; i <= m && cnt < n - 1; ++i) { int x = get(t[i].x),y = get(t[i].y); if(x != y) { vis[i] = true; sum += t[i].v; fa[x] = y; cnt++; add(t[i].x,t[i].y,t[i].v); add(t[i].y,t[i].x,t[i].v); } } } void BFS() { queue < int > q; q.push(1); deep[1] = 1; while(!q.empty()) { int x = q.front(); q.pop(); for(int i = lin[x],y; i ; i = e[i].next) { if(deep[y = e[i].y]) continue; deep[y] = deep[x] + 1; f[y][0] = x; dis1[y][0] = e[i].v; dis2[y][0] = -INF; q.push(y); for(int j = 1; j <= 20; ++j) { f[y][j] = f[f[y][j - 1]][j - 1]; dis1[y][j] = max(dis1[y][j - 1],dis1[f[y][j - 1]][j - 1]); if(dis1[y][j - 1] == dis1[f[y][j - 1]][j - 1]) dis2[y][j] = max(dis2[y][j - 1],dis2[f[y][j - 1]][j - 1]); if(dis1[y][j - 1] > dis1[f[y][j - 1]][j - 1]) dis2[y][j] = max(dis2[y][j - 1],dis1[f[y][j - 1]][j - 1]); if(dis1[y][j - 1] < dis1[f[y][j - 1]][j - 1]) dis2[y][j] = max(dis1[y][j - 1],dis2[f[y][j - 1]][j - 1]); } } } } int lca(int x,int y) { if(deep[x] > deep[y]) swap(x,y); for(int i = 20; i >= 0; --i) if(deep[f[y][i]] >= deep[x]) y = f[y][i]; if(x == y) return x; for(int i = 20; i >= 0; --i) if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i]; return f[x][0]; } void cal(int x, int fa, int v) { int val1 = 0, val2 = 0; for(int i = 20; i >= 0; --i) { if(deep[f[x][i]] >= deep[fa]) { if (dis1[x][i] > val1) { val2 = val1; val1 = dis1[x][i]; } val2 = max(val2, dis2[x][i]); x = f[x][i]; } } if (val1 != v) tem = min(tem, v - val1); else tem = min(tem, v - val2); } int main() { n = read(); m = read(); for(int i = 1; i <= m; ++i) { t[i].x = read(); t[i].y = read(); t[i].v = read(); } Kruskal(); BFS(); for(int i = 1; i <= m; ++i) if(!vis[i]) { int x = t[k].x,y = t[k].y; int fat = lca(x,y); cal(x,fat,v); cal(y,fat,v); } printf("%lld\n",sum + tem); return 0; }
.