洛谷P4180 题解

题目链接

\(Solution:\)

看到标题,想到\(MST\)
我们再考虑次小生成树,在原来的\(MST\)加上一条非树边\((u,v)\),会跟原本\(MST\)上连接\((u,v)\)的路径形成一个环
这启发我们考虑在环上寻找次小值
我们维护一个\(DP\)数组\(g[i][j][0/1]\)表示\(i\)\(i\)\(2^j\)倍祖先路径上的最小,次小值,有状态转移方程:
\(g[i][j][0]=max(g[f[i][j-1]][j-1][0],g[i][j-1][0])\)

\[g[i][j][1]= \begin{cases} max(g[f[i][j-1]][j-1][1],g[i][j-1][1])& \text{g[i][j-1][0]==g[f[i][j-1]][j-1][0]}\\ max(g[f[i][j-1]][j-1][1],g[i][j-1][0])& \text{g[i][j-1][0]<g[f[i][j-1]][j-1][0]}\\ max(g[f[i][j-1]][j-1][0],g[i][j-1][1])& \text{g[i][j-1][0]>g[f[i][j-1]][j-1][0]} \end{cases}\]
每次我们枚举一条非树边\((u,v)\),令\(LCA=lca(u,v)\),用倍增查询\(u\)\(LCA\)\(v\)\(LCA\)的次小值,即为我们所查询的环上的最小值

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e17;
ll n,m,u,v,w,cnt,head[1000050],f[1000050][30],g[1000050][30][2],dep[1000050],fa[1000050],vis[2000050],ans;
struct edge
{
    ll to,nxt,w;
}e[1000050];
struct Edge
{
    ll u,v,w;
}ed[1000050];
bool operator<(Edge a,Edge b)
{
    return a.w<b.w;
}
inline ll find(ll x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void add(ll u,ll v,ll w)
{
    cnt++;
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    e[cnt].w=w;
    head[u]=cnt;
}
void dfs(ll u,ll fa)
{
    dep[u]=dep[fa]+1;
    f[u][0]=fa;
    for(ll i=head[u];i;i=e[i].nxt)
    {
        ll v=e[i].to;
        if(v==fa) continue;
        f[v][0]=u;
        g[v][0][0]=e[i].w;
        g[v][0][1]=-INF;
        dfs(v,u);
    }
}
ll lca(ll x,ll y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(ll i=20;i>=0;i--)
    {
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
    }
    for(ll i=20;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
ll up(ll u,ll v,ll w)
{
    ll res=-INF;
    for(ll i=20;i>=0;i--)
    {
        if(dep[f[u][i]]>=dep[v])
        {
            if(w==g[u][i][0]) res=max(res,g[u][i][1]);
            else res=max(res,g[u][i][0]);
            u=f[u][i];
        }
    }
    return res;
}
ll work(ll i)
{
    ll u=ed[i].u,v=ed[i].v,rt=lca(u,v);
    ll tmp1=up(u,rt,ed[i].w),tmp2=up(v,rt,ed[i].w);
    return ed[i].w-max(tmp1,tmp2);
}
int main(void)
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        ed[i].u=u,ed[i].v=v,ed[i].w=w;
    }
    sort(ed+1,ed+m+1);
    for(ll i=1;i<=n;i++) fa[i]=i;
    ll tmp=0;
    for(ll i=1;i<=m;i++)
    {
        ll a=ed[i].u,b=ed[i].v;
        ll fx=find(a),fy=find(b);
        if(fx==fy) continue;
        fa[fx]=fy;
        vis[i]=1;
        ans+=ed[i].w,tmp++;
        add(a,b,ed[i].w);
        add(b,a,ed[i].w);
        if(tmp==n-1) break;
    }
    dfs(1,0);
    g[1][0][1]=-INF;
    for(ll j=1;j<=20;j++)
    {
        for(ll i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j][0]=max(g[f[i][j-1]][j-1][0],g[i][j-1][0]);
            if(g[i][j-1][0]==g[f[i][j-1]][j-1][0]) g[i][j][1]=max(g[f[i][j-1]][j-1][1],g[i][j-1][1]);
            else if(g[i][j-1][0]<g[f[i][j-1]][j-1][0]) g[i][j][1]=max(g[f[i][j-1]][j-1][1],g[i][j-1][0]);
            else g[i][j][1]=max(g[f[i][j-1]][j-1][0],g[i][j-1][1]);
         } 
    }
    ll haha=INF;
    for(ll i=1;i<=m;i++)
    {
        if(!vis[i])
        {
            haha=min(haha,ans+work(i));
        }
    }
    printf("%lld\n",haha);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lgj-lgj/p/12334912.html