bzoj1977 [BeiJing2010组队]次小生成树 Tree

Posted llppdd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1977 [BeiJing2010组队]次小生成树 Tree相关的知识,希望对你有一定的参考价值。

[BeiJing2010组队]次小生成树 Tree

Time Limit: 10 Sec Memory Limit: 512 MB

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 。

实际上你要先求出最小生成树,然后再在这个基础上改一点点就变成了次小生成树。
具体来说就是找一条以前没有用的边,然后把这两点原来路径上的一条最大的边给砍了。。。
然后题目很变态的要求严格小于。。。你还要记第二小的边。。。。
最开始偷懒用set。。。被卡常了。。。
然后最后4分钟卡常大师重出江湖。。。。一手sort直接*过。。。




#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
struct lpl{
    int from, to, dis;
    bool flag;
}lin;
struct ld{
    int to, dis;
}qwe;
struct lpd{
    int fa, dis, deep, tree[18][3];
}node[maxn];
int n, m, root, pw[25], fa[maxn];
int tot, aaa[maxn];
long long sum = 0, ans = 1e14 + 5;
set<int> s;
set<int>::iterator iter;
vector<lpl> edge;
vector<ld> point[maxn];

inline int read()
{  
   int s = 0, w = 1;  char ch = getchar();  
   while(ch <= '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}  
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();  
   return s * w;  
}  

inline bool cmp(lpl A, lpl B){return A.dis < B.dis;}

int find(int t){return t == fa[t] ? t : fa[t] = find(fa[t]);}

inline void putit()
{
    n = read(); m = read(); root = 1; pw[0] = 1;
    for(int a, b, i = 1; i <= m; ++i){
        scanf("%d%d%d", &lin.from, &lin.to, &lin.dis); edge.push_back(lin);
    }
    for(int i = 1; i <= 17; ++i) pw[i] = pw[i - 1] * 2;
}

inline void kruskal()
{
    for(int i = 1; i <= n; ++i) fa[i] = i;
    sort(edge.begin(), edge.end(), cmp); int N = edge.size() - 1;
    for(int A, B, i = 0; i <= N; ++i){
        A = find(edge[i].from); B = find(edge[i].to);
        if(A == B) continue;
        fa[A] = B; qwe.to = edge[i].to; qwe.dis = edge[i].dis; 
        sum += (long long)qwe.dis; edge[i].flag = true;
        point[edge[i].from].push_back(qwe); qwe.to = edge[i].from;
        point[edge[i].to].push_back(qwe);
    }
}

void build(int t)
{
    for(int i = point[t].size() - 1; i >= 0; --i){
        int now = point[t][i].to;
        if(now == node[t].fa) continue;
        node[now].fa = t; node[now].dis = point[t][i].dis; node[now].deep = node[t].deep + 1;
        build(now);
    }
}

void prepare(int t)
{
    node[t].tree[0][0] = node[t].fa; node[t].tree[0][1] = node[t].dis;
    for(int i = 1; i <= 17; ++i){
        if(pw[i] > node[t].deep) break;
        node[t].tree[i][0] = node[node[t].tree[i - 1][0]].tree[i - 1][0];
//      s.insert(node[node[t].tree[i - 1][0]].tree[i - 1][1]);
//      s.insert(node[node[t].tree[i - 1][0]].tree[i - 1][2]);
//      s.insert(node[t].tree[i - 1][1]);
//      s.insert(node[t].tree[i - 1][2]);
//      for(iter = s.begin(); iter != s.end(); ++iter){
//          node[t].tree[i][2] = node[t].tree[i][1]; node[t].tree[i][1] = *iter;
//      }
//      s.clear();
        tot = 0;
        aaa[++tot] = node[node[t].tree[i - 1][0]].tree[i - 1][1];
        aaa[++tot] = node[node[t].tree[i - 1][0]].tree[i - 1][2];
        aaa[++tot] = node[t].tree[i - 1][1];
        aaa[++tot] = node[t].tree[i - 1][2];
        sort(aaa + 1, aaa + tot + 1); node[t].tree[i][1] = aaa[tot];
        for(int k = tot - 1; k >= 1; --k){
            if(aaa[k] != node[t].tree[i][1]){
                node[t].tree[i][2] = aaa[k]; break;
            }
        }
    }
    for(int now, i = point[t].size() - 1; i >= 0; --i){
        now = point[t][i].to;
        if(now == node[t].fa) continue;
        prepare(now);
    }
}

inline void workk()
{
    for(int A, B, lca, mx, i = edge.size() - 1; i >= 0; --i){
        lin = edge[i]; A = lin.from; B = lin.to; mx = 0; tot = 0;
        if(edge[i].flag) continue;
        if(node[A].deep < node[B].deep) swap(A, B);
        for(int k = 17; k >= 0; --k){
            if(node[A].deep == node[B].deep) break;
            if(node[node[A].tree[k][0]].deep >= node[B].deep && node[A].tree[k][0]){
                //s.insert(node[A].tree[k][1]); s.insert(node[A].tree[k][2]);
                aaa[++tot] = node[A].tree[k][1]; aaa[++tot] = node[A].tree[k][2];
                A = node[A].tree[k][0]; 
            }
        }
        if(A != B){
            for(int k = 17; k >= 0; --k){
                if(node[A].tree[k][0] != node[B].tree[k][0]){
//                  s.insert(node[A].tree[k][1]); s.insert(node[A].tree[k][2]);
//                  s.insert(node[B].tree[k][1]); s.insert(node[B].tree[k][2]);
                    aaa[++tot] = node[A].tree[k][1]; aaa[++tot] = node[A].tree[k][2];   
                    aaa[++tot] = node[B].tree[k][1]; aaa[++tot] = node[B].tree[k][2];
                    A = node[A].tree[k][0]; B = node[B].tree[k][0];
                }
            }   
            aaa[++tot] = node[A].dis; aaa[++tot] = node[B].dis;
            //s.insert(node[A].dis); s.insert(node[B].dis); 
            lca = node[A].fa;       
        }
        if(A == B) lca = A;
//      for(iter = s.begin(); iter != s.end(); ++iter){
//          if(*iter != lin.dis) mx = max(mx, *iter);
//      }
//      s.clear();
        sort(aaa + 1, aaa + 1 + tot);
        for(int i = tot; i >= 1; --i){
            if(aaa[i] != lin.dis){
                mx = aaa[i]; break;
            }
        }
        ans = min(ans, sum + lin.dis - mx);
    }
}

int main()
{
    putit();
    kruskal();
    build(root);
    prepare(root);
    workk();
    cout << ans;
    return 0;
}

以上是关于bzoj1977 [BeiJing2010组队]次小生成树 Tree的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 1977 [BeiJing2010组队]次小生成树 Tree

BZOJ 1977 [BeiJing2010组队]次小生成树 Tree

严格次小生成树(Bzoj1977:[Beijing2010组队]次小生成树)

bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树

bzoj1977[BeiJing2010组队]次小生成树 Tree 权值线段树合并

1977: [BeiJing2010组队]次小生成树 Tree