BZOJ 1977 次小生成树(最近公共祖先)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1977

题意:求一棵树的严格次小生成树,即权值严格大于最小生成树且权值最小的生成树。

思路:若现在已经得到了最小生成树,那么若 添加一条边E,就会得到一个环,我们只需要去掉环上权值小于E且最大的一条边就会得到另一棵较优的生成树。因此,只需要枚举不在生成树上的边,计算将其添 加到最小生成树中得到的新生成树的权值。取最小值即可。那么,现在的问题就是在一个圈中找到一个最大的小于新添加的边的权值的边。由于最小生成树是一棵 树,可以利用最近公共祖先,并记录路径上权值的最大和次大值。则新加入的边若大于圈上其他边的最大值X,删掉X即可;否则掉圈上的次小值。

struct node
{
    int u,v,w,flag;
};

node a[N<<2];
vector<pair<int,int> > g[N];
int f[N][20],dp1[N][20],dp2[N][20];
int n,m;

int cmp(node a,node b)
{
    return a.w<b.w;
}

int p[N];

int get(int x)
{
    if(p[x]!=x) p[x]=get(p[x]);
    return p[x];
}

void add(int u,int v,int w)
{
    g[u].pb(MP(v,w));
    g[v].pb(MP(u,w));
}

int dep[N];

void DFS(int u,int pre)
{
    int i,v;
    FOR0(i,SZ(g[u]))
    {
        v=g[u][i].first;
        if(v==pre) continue;
        f[v][0]=u;
        dp1[v][0]=g[u][i].second;
        dp2[v][0]=-1;
        dep[v]=dep[u]+1;
        DFS(v,u);
    }
}

void up(int &x,int y)
{
    if(x==-1) x=y;
    else if(x<y) x=y;
}

void init()
{
    int i,j;
    for(i=1;(1<<i)<=n;i++)
    {
        for(j=1;j+(1<<i)-1<=n;j++)
        {
            f[j][i]=f[f[j][i-1]][i-1];
            dp1[j][i]=max(dp1[j][i-1],dp1[f[j][i-1]][i-1]);
            dp2[j][i]=-1;
            if(dp1[j][i-1]<dp1[j][i]) up(dp2[j][i],dp1[j][i-1]);
            if(dp1[f[j][i-1]][i-1]<dp1[j][i]) up(dp2[j][i],dp1[f[j][i-1]][i-1]);
            if(dp2[j][i-1]!=-1) up(dp2[j][i],dp2[j][i-1]);
            if(dp2[f[j][i-1]][i-1]!=-1) up(dp2[j][i],dp2[f[j][i-1]][i-1]);
        }
    }
}

void up(int x,int &a,int &b)
{
    if(x>a) b=a,a=x;
    else if(x<a&&x>b) b=x;
}

int deal(int x,int a,int b)
{
    if(x==a)
    {
        if(b==-1) return -1;
        return x-b;
    }
    if(a==-1) return -1;
    return x-a;
}

i64 cal(int t)
{
    int u=a[t].u,v=a[t].v,w=a[t].w;
    int Max1=-1,Max2=-1;
    if(dep[u]>dep[v]) swap(u,v);
    int x=dep[v]-dep[u];
    int i;
    for(i=0;i<20;i++) if(x&(1<<i))
    {
        up(dp1[v][i],Max1,Max2);
        up(dp2[v][i],Max1,Max2);
        v=f[v][i];
    }
    if(u==v) return deal(w,Max1,Max2);

    for(i=19;i>=0;i--)
    {
        if(f[u][i]&&f[v][i]&&f[u][i]!=f[v][i])
        {
            up(dp1[u][i],Max1,Max2);
            up(dp2[u][i],Max1,Max2);
            up(dp1[v][i],Max1,Max2);
            up(dp2[v][i],Max1,Max2);
            u=f[u][i];
            v=f[v][i];
        }
    }
    up(dp1[u][0],Max1,Max2);
    up(dp2[u][0],Max1,Max2);
    up(dp1[v][0],Max1,Max2);
    up(dp2[v][0],Max1,Max2);
    return deal(w,Max1,Max2);
}

int main()
{
    RD(n,m);
    int i;
    FOR1(i,m)
    {
        RD(a[i].u,a[i].v,a[i].w);
        a[i].flag=1;
    }
    sort(a+1,a+m+1,cmp);
    FOR1(i,n) p[i]=i;
    i64 ans=0;
    int u,v;
    FOR1(i,m)
    {
        u=get(a[i].u);
        v=get(a[i].v);
        if(u==v) continue;
        a[i].flag=0;
        ans+=a[i].w;
        add(a[i].u,a[i].v,a[i].w);
        p[u]=v;
    }
    DFS(1,-1);
    init();
    i64 k=inf,temp;
    FOR1(i,m) if(a[i].flag)
    {
        temp=cal(i);
        if(temp!=-1&&temp<k) k=temp;
    }
    PR(ans+k);
}

BZOJ 1977 次小生成树(最近公共祖先),布布扣,bubuko.com

时间: 2024-08-03 20:29:39

BZOJ 1977 次小生成树(最近公共祖先)的相关文章

BZOJ 1977 次小生成树

TM终于过了.... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxv 300500 #define maxe 800500 #define inf 0x7fffffffffffffff using namespace std; struct edge { long long v,w,nxt; }e[maxe]; struct edge

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

1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 2313  Solved: 544 [Submit][Status][Discuss] Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格

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

严格次小生成树.一开始没有特批一圈都相等的情况,一直WA,十分难受. 先生成最小生成树,枚举每条非树边,连上它构成一个环,拆掉环上树边中最大的一条(若和该边相等则次大的一条)换上这条. 用倍增维护一条链上的最大边和次大边,倍增跑lca同时找出环上最大边和次大边,看能否更新答案. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath

bzoj 1977 洛谷P4180 严格次小生成树

Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z. Output: 包含一行,仅一个数,表示严格次小生成树的边权和.(数据保证必定存在严格次小生成树) 思路:先求出原图的最小生成树,然后继续从小到大枚举边(x,y),对于x,y用倍

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

题解:和cf的一道题比较类似 首先跑一个MST 对于这个树做树链剖分 枚举不在这个树上的边找严格小于这条边的最大边权值 然后求ans #include <bits/stdc++.h> #define ll long long const int MAXN=1e5+10; const int maxn=3e5+10; const int inf=1e9+20; using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(

D5 LCA 最近公共祖先

第一题: POJ 1330 Nearest Common Ancestors POJ 1330 这个题可不是以1为根节点,不看题就会一直wa呀: 加一个找根节点的措施: #include<algorithm> #include<bitset> #include<cctype> #include<cerrno> #include<clocale> #include<cmath> #include<complex> #incl

树的问题小结(最小生成树、次小生成树、最小树形图、LCA、最小支配集、最小点覆盖、最大独立集)

树的定义:连通无回路的无向图是一棵树. 有关树的问题: 1.最小生成树. 2.次小生成树. 3.有向图的最小树形图. 4.LCA(树上两点的最近公共祖先). 5.树的最小支配集.最小点覆盖.最大独立集. 一.最小生成树 解决的问题是:求无向图中边权值之和最小的生成树. 算法有Kruskal和Prim. Kruskal使用前向星和并查集实现,可以存储重边(平行边),时间复杂度是O(m log m  +  m),m是边的数量. Prim使用邻接矩阵建图,不可以存储重边(平行边),如果出现重边,存储的

【洛谷P3379】【模板】最近公共祖先(LCA)

题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1: 5 5 4 3 1 2 4 5

bzoj 1977

题意:求严格的次小生成树.点n<=100000,m<=300000 思路:很容易想到先做一边最小生成树,然后枚举每条非树边(u, v, w),然后其实就是把u,v路径上小于w的最大边替换成w,对于所有的这种新树取一个权值最小的即可.. 然后就变成求u,v的最大值及次大值..树链剖分和lct显然是可以做的.. 不过很早就知道倍增却一直没写过,今天就正好写一发.. code: 1 /**********************************************************