zoukankan      html  css  js  c++  java
  • P4180 【模板】严格次小生成树[BJWC2010]

    传送门

    次小生成树

    那肯定是最小生成树上改一条边(改两条肯定不如只改其中一条小)

    那就枚举所有不在最小生成树上的边

    考虑如果把此边加入,另一边删除后的情况

    考虑要删哪条边后才能保持树的形态,并且总长最小

    加入一条边后树就会出现一个环

    那么删掉的边要在加入的边连接的两点间的路径上

    并且删去的边要尽量大

    那就可以用LCA来求出树上两点间路径上的最长边

    但是现在还有一个问题,可能删去的边和加入的边一样长

    所以还要维护一下次长的边

    次长边维护也不难,具体看代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    inline int read()
    {
        int x=0; char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x;
    }
    
    const int N=1e5+7;
    const int M=3e5+7;
    int n,m;
    int fa[N];
    long long s;
    inline int find(int x){ return fa[x]==x ? x : fa[x]=find(fa[x]); }
    struct edge
    {
        int a,b,c;
    }e[M];
    inline bool cmp(const edge &a,const edge &b){ return a.c<b.c; }
    
    vector <int> v[N],g[N];
    bool p[M];
    int f[N][27],dep[N];
    long long mx[N][27],mxx[N][27];//mxx存次长边
    void dfs(int x,int father)
    {
        dep[x]=dep[father]+1; f[x][0]=father;
        for(int i=1;i<=20;i++)
        {
            f[x][i]=f[f[x][i-1]][i-1];
            mx[x][i]=max(mx[f[x][i-1]][i-1],mx[x][i-1]);
            mxx[x][i]=max(mxx[f[x][i-1]][i-1],mxx[x][i-1]);
    
            if(mx[ f[x][i-1] ][i-1]>mx[x][i-1])
                mxx[x][i]=max(mxx[x][i],mx[x][i-1]);
            if(mx[x][i-1]>mx[ f[x][i-1] ][i-1])
                mxx[x][i]=max(mxx[x][i],mx[ f[x][i-1] ][i-1]);
        }
        int len=v[x].size();
        for(int i=0;i<len;i++)
        {
            int u=v[x][i];
            if(u==father) continue;
            mx[u][0]=g[x][i]; mxx[u][0]=-1;
            dfs(u,x);
        }
    }
    inline long long LCA(int x,int y,int z)//询问最长边或者次长边
    {
        long long res=-1e17+7;
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=20;i>=0;i--)
            if(dep[f[x][i]]>=dep[y])
            {
                if(mx[x][i]!=z) res=max(res,mx[x][i]);
                else res=max(res,mxx[x][i]);
                x=f[x][i];
            }
        if(x==y) return res;
        for(int i=20;i>=0;i--)
            if(f[x][i]!=f[y][i])
            {
                if(mx[x][i]!=z) res=max(res,mx[x][i]);
                else res=max(res,mxx[x][i]);
                if(mx[y][i]!=z) res=max(res,mx[y][i]);
                else res=max(res,mxx[y][i]);
                x=f[x][i]; y=f[y][i];
            }
        if(mx[x][0]!=z) res=max(res,mx[x][0]);
        else res=max(res,mxx[x][0]);
        if(mx[y][0]!=z) res=max(res,mx[y][0]);
        else res=max(res,mxx[y][0]);
        return res;
    }
    
    int main()
    {
        n=read(); m=read();
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++)
            e[i].a=read(),e[i].b=read(),e[i].c=read();
    
        sort(e+1,e+m+1,cmp);
        int tot=0;
        for(int i=1;i<=m;i++)
        {
            int xa=find(e[i].a),xb=find(e[i].b);
            if(xa==xb) continue;
            s+=e[i].c; fa[xa]=xb;  p[i]=1;
            v[e[i].a].push_back(e[i].b); g[e[i].a].push_back(e[i].c);
            v[e[i].b].push_back(e[i].a); g[e[i].b].push_back(e[i].c);
            if(++tot==n-1) break;
        }//先求出最小生成树
    
        dfs(1,1);
        long long ans=1e17+7;
        for(int i=1;i<=m;i++)
        {
            if(p[i]) continue;//枚举所有没有加入最小生成树的边
            long long t=LCA(e[i].a,e[i].b,e[i].c);//找出最长边或者次长边
            if(t==e[i].c) continue;
            ans=min(ans,s-t+e[i].c);
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    遇到项目上面有叉,但是找不到错误的原因
    遇到build的问题
    遇到scan configurtation CDT builder等的错误
    遇到attemp to invoke virtual method
    遇到looper之类关于消息循环的
    Linux与Windows信息交互快捷方法
    并行查询
    PostgreSQL 事务管理的MVCC
    Linux安装memcached
    Linux 安装 Redis
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9695527.html
Copyright © 2011-2022 走看看