bzoj1977 严格的次小生成树(LCA倍增)

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

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 2304   Solved: 542
[ 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 。


所谓严格的次小是指权值严格大于最小生成树的次小生成树,我们知道一般次小生成树,只需要先用kruskal算法求得最小生成树,然后暴力枚举非树边,替换路径最大边即可。

这题也可以类似思考,只是有一个问题,如果最大边与当前枚举边相等时,我们不能替换,于是求其次用次小边来替换。这样我们需要求得路径上的最小边和次小边(小于最小边),于是我们可以利用LCA的倍增算法来维护。

预处理过程需要考虑i->f[i][j]与f[i][j]->f[f[i][j]][j]这两段的合并,考虑这两段的最大值相同与不同情况,相同则说明次大值是这两个的次大值的最大值,不同的话,假设(a,b),(c,d)表示两段的(最大,次大),若a>c,显然次大为max(b,c), c>a的情况类似,见代码中的函数ck1。

预处理完,维护沿单链向上跳,记单链的(最大,次大)为(a,b),当前得到最优值(lx,ln),分三种情况讨论,lx与a的大小关系,见代码中的函数ck3。

这题数据较弱,其实之前的写法有点小问题也AC了,后来仔细考虑修改后,继续AC,修改后的更趋于正解。

代码:

#include
#include
#include
#include
#define Maxn 300010
using namespace std;

struct edge{
    int to,w,next;
}p[Maxn];
int head[Maxn/3],tot;
void addedge(int a,int b,int c){
    p[tot].to=b;
    p[tot].w=c;
    p[tot].next=head[a];
    head[a]=tot++;
}
struct line{
    int u,v,w;
    bool operator<(const line &a)const{
        return we) {swap(c,e);swap(d,f);} //c>=1;
    }
    if(u==v) return ck2(lx,ln,w);
    for(int i=18;i>=0;i--){
        if(f[u][i]!=f[v][i]){
            ck3(lx,ln,u,i);
            ck3(lx,ln,v,i);
            u=f[u][i];
            v=f[v][i];
        }
    }
    ck3(lx,ln,u,0);
    ck3(lx,ln,v,0);
    return ck2(lx,ln,w);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i


你可能感兴趣的:(树上倍增)