1977: [BeiJing2010组队]次小生成树 Tree
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 3916 Solved: 1133
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 。
解析:
我们知道,如果往最小生成树里加一条边一定能形成一个环,这时从环中选一个非新加入边的最大边权的边拿掉,就形成了一个次小生成树,于是解法就明显了。
1.做一遍最小生成树并将用到的边建成一棵树。
2.dfs求出d1[ i ][ j ],d2[ i ][ j ]分别表示从点 i 向上走 2^j 个单位沿途中第一、第二大的边权,因为题目说严格次小所以不能相等。
3.对于每条不在最小生成树上的边,求出两点间的LCA,分别记录最大边权更新答案。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=101000;
int n,m,size,s,minn=1e9;
long long ans;
int father[Max],f[Max][20],d1[Max][20],d2[Max][20],first[Max],depth[Max];
struct shu{int to,next,len;};
shu edge[Max*6];
struct tree{int x,y,len,v;};
tree Edge[Max*3];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline bool comp(const tree &a,const tree &b){return a.len < b.len;}
inline int getfather(int v){return father[v] == v ? v : father[v] =getfather(father[v]);}
inline void build(int x,int y,int z){edge[++size].next=first[x],first[x]=size,edge[size].to=y,edge[size].len=z;}
inline int mn(int x,int y){return x > y ? y : x;}
inline int mx(int x,int y){return x > y ? x : y;}
inline void dfs(int point,int fa)
{
for(int i=1;i<=16;i++)
if(depth[point] >= 1<<i)
{
f[point][i] = f[f[point][i-1]][i-1];
d1[point][i] = mx(d1[point][i-1] , d1[f[point][i-1]][i-1]);
if(d1[point][i-1] == d1[f[point][i-1]][i-1]) d2[point][i] = mx(d2[point][i-1] , d2[f[point][i-1]][i-1]);
else
{
d2[point][i] = mn(d1[point][i-1] , d1[f[point][i-1]][i-1]);
d2[point][i] = mx(d2[point][i] , d2[f[point][i-1]][i-1]);
}
}
else break;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(to == fa) continue;
f[to][0] = point;
depth[to] = depth[point] + 1;
d1[to][0] = edge[u].len;
dfs(to,point);
}
}
inline int LCA(int x,int y)
{
if(depth[x] < depth[y]) swap(x,y);
int len = depth[x] - depth[y];
for(int i=16;i>=0;i--)
if(len >= 1<<i) len-=1<<i,x = f[x][i];
if(x == y) return x;
for(int i=16;i>=0;i--)
if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i];
return f[x][0];
}
inline void search(int x,int fa,int len)
{
int max1=0,max2=0,h =depth[x] - depth[fa];
for(int i=16;i>=0;i--)
if(h >= 1<<i)
{
h-=1<<i;
if(d1[x][i] > max1) max2 = max1,max1 = d1[x][i];
max2 = mx(max2 , d2[x][i]);
x=f[x][i];
}
if(len != max1) minn = mn(minn , len - max1);
else minn = mn(minn , len - max2);
}
inline void solve(int x,int y,int z)
{
int fa = LCA(x,y);
search(x,fa,z),search(y,fa,z);
}
int main()
{
n=get_int();
m=get_int();
for(int i=1;i<=n;i++) father[i] = i;
for(int i=1;i<=m;i++) Edge[i].x=get_int(),Edge[i].y=get_int(),Edge[i].len=get_int();
sort(Edge+1,Edge+m+1,comp);
for(int i=1;i<=m;i++)
{
int fax = getfather(Edge[i].x),fay = getfather(Edge[i].y);
if(fax != fay)
{
ans += Edge[i].len;
father[fay] = fax;
Edge[i].v = 1;
build(Edge[i].x,Edge[i].y,Edge[i].len);
build(Edge[i].y,Edge[i].x,Edge[i].len);
s++;
if(s == n-1) break;
}
}
dfs(1,0);
for(int i=1;i<=m;i++)
if(!Edge[i].v) solve(Edge[i].x,Edge[i].y,Edge[i].len);
cout<<ans+minn<<"\n";
return 0;
}