luogu P4180 模板严格次小生成树[BJWC2010]

Posted cnyali-xwx

tags:

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

思路:

首先要想清楚一个问题,对于次小生成树,肯定是在最小生成树上断掉一条边,然后在非最小生成树边中加一条边进去产生的(具体窝也不会证鸭;知道了这个非常显然的结论后具体思路就好想了.

做法:

我们考虑暴力删边和加边.对于每条未在最小生成树中的边,考虑删掉最小生成树中的一条边(边权尽可能大)并加入此边,因为非最小生成树的边权肯定都大于等于最小生成树边的边权,所以删掉的边边权越大,新树的总边权才会越小,并且要保证新树还是生成树.

所以我们先求出最小生成树记录下总边权值,然后对于每一条非最小生成树边的两个节点在最小生成树上求一次\(lca\)(保证新树是生成树),然后求出节点到\(lca\)的最大边权和次大边权(因为最大边权可能和要加进去的边边权相等不满足严格次小生成树,所以要多求一个次小边权).然后用总边权减去求得最大边权(或次大边权)再加上要加进去的边的边权,记录答案最小值即可.

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=100000+5;
const int M=300000+5;
const int logs=19;
const int INF=2147483647*1e9;
int n,m,tot,cnt,sum;
int head[N],dep[N],pre[N][logs+1],vis[M],fa[N],zd[N][logs+1],cd[N][logs+1];
struct Edge
    int next,to,dis;
e[M*2];
struct node
    int u,v,w;
a[M*2];
int find(int x)return x==fa[x] ? fa[x] : fa[x]=find(fa[x]);
inline void merge(int x,int y)fa[x]=y;
inline void add_edge(int from,int to,int dis)
    e[++tot].next=head[from];
    e[tot].to=to;
    e[tot].dis=dis;
    head[from]=tot;

bool cmp(node a,node b)return a.w<b.w;
void dfs(int now,int faa)
    pre[now][0]=faa;
    dep[now]=dep[faa]+1;
    for(int i=1;i<=logs;i++)
        pre[now][i]=pre[pre[now][i-1]][i-1];
        zd[now][i]=max(zd[now][i-1],zd[pre[now][i-1]][i-1]);//最大值
        cd[now][i]=max(cd[now][i-1],cd[pre[now][i-1]][i-1]);//次大值
        if(zd[now][i-1]>zd[pre[now][i-1]][i-1]) cd[now][i]=max(cd[now][i],zd[pre[now][i-1]][i-1]);//这里次大值可能与两个最大值中较小的那个更新
        else if(zd[now][i-1]<zd[pre[now][i-1]][i-1]) cd[now][i]=max(cd[now][i],zd[now][i-1]);
    //倍增处理祖先,最大值,次大值.
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=faa) dfs(e[i].to,now);

void cx(int x,int faa)
    for(int i=head[x];i;i=e[i].next)
        int v=e[i].to;
        if(v==faa) continue;
        zd[v][0]=e[i].dis;
        cd[v][0]=-INF;
        cx(v,x);
    
//预处理最大值次大值数组
inline int lca(int x,int y,int w)
    int ans=-INF;
    if(dep[x]<dep[y]) std::swap(x,y);
    for(int i=logs;i>=0;i--)
        if(dep[pre[x][i]]>=dep[y])
            if(zd[x][i]==w) ans=max(ans,cd[x][i]);
            else ans=max(ans,zd[x][i]);
            x=pre[x][i];
        
    if(x==y) return ans;
    for(int i=logs;i>=0;i--)
        if(pre[x][i]!=pre[y][i])
            if(w==zd[x][i]) ans=max(ans,cd[x][i]);
            else ans=max(ans,zd[x][i]);
            if(w==zd[y][i]) ans=max(ans,cd[y][i]);
            else ans=max(ans,zd[y][i]);
            x=pre[x][i],y=pre[y][i];
        
    if(w==zd[x][0]) ans=max(ans,cd[x][0]);
    else ans=max(ans,zd[x][0]);
    if(w==zd[y][0]) ans=max(ans,cd[y][0]);
    else ans=max(ans,zd[y][0]);
    return ans;
//这里直接在求lca的过程中把最大值和次大值求出来.
signed main()
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++) scanf("%lld%lld%lld",&a[i].u,&a[i].v,&a[i].w);
    sort(a+1,a+1+m,cmp);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
        int x=find(a[i].u),y=find(a[i].v);
        if(x!=y)
            ++cnt;
            merge(x,y);
            sum+=a[i].w;
            add_edge(a[i].u,a[i].v,a[i].w);
            add_edge(a[i].v,a[i].u,a[i].w);
            vis[i]=1;//标记最小生成树边
            if(cnt==n-1) break;
        
    //最小生成树
    cx(1,0);
    dfs(1,0);//预处理
    int ans=INF;
    for(int i=1;i<=m;i++)
        if(!vis[i])
            int k=lca(a[i].u,a[i].v,a[i].w);
            ans=min(ans,sum-k+a[i].w);
        
    
    printf("%lld",ans);
    return 0;

以上是关于luogu P4180 模板严格次小生成树[BJWC2010]的主要内容,如果未能解决你的问题,请参考以下文章

P4180 模板严格次小生成树[BJWC2010](严格次小生成树)

洛谷 P4180 模板严格次小生成树[BJWC2010]次小生成树

P4180 模板严格次小生成树[BJWC2010]

P4180 严格次小生成树[BJWC2010]

P4180 模板严格次小生成树[BJWC2010]

P4180 模板严格次小生成树[BJWC2010]