【BZOJ1977】次小生成树 Tree

                     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 
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<=0;i--)
      if(len >= 1<=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< 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<

 

你可能感兴趣的:(次小生成树,BZOJ)