hszxoj 货车运输 [lca]

题目链接: hszxoj 货车运输

题目描述与思路

简化题目:

\(x\)\(y\) 两点间路径的边权最小值的最大值

与之前的最短路最大的不同是这道题是多源最短路,那么 \(spfa\) 就废了,\(Floyd\) 定会 \(TLE\) 所以就需要用新的算法。

\(lca\) 一定是在树上的,但明显这玩意他既有环又有森林,直接用就会 \(RE\)

所以我们需要用到 \(kru\) 重构树

首先展示 \(kru\) 代码

void kru()
{
    stable_sort(e+1,e+1+m,cmp);
    cnt=n;
    for(int i=1;i<=m;i++)
    {
        int a=find(e[i].x),b=find(e[i].y);
        if(a==b) continue;
        f[a]=f[b]=++cnt;
        g[a].push_back(cnt),g[cnt].push_back(a),
        g[b].push_back(cnt),g[cnt].push_back(b);
        w[cnt]=e[i].w;
        if(++tot==n-1) break;
    }
    root=n+tot;
}

首先排序一下,注意这道题是 \(最大生成树\) ,所以从大到小排序,这样求出来才是最小值的最大值,不然就是最大值的最小值了

有三个重要点

  1. 和正常的最小生成树不同,我们最后只需要输出两点的最近公共祖先的点权,但我们实则求的是对应的边权,就需要将边权转换为点权。不妨将相连的两点 \(x\)\(y\) 中间插入一个点,让这个点的点权为 \(x,y\) 的边权,并使这个点成为 \(x\)\(y\) 的祖先
    • 为什么让他成为这两个点的祖先也是显而易见的,我们最后输出最近公共祖先的权值,只有类似这个新点的权值,他才不是 \(0\),看完全部代码后会理解得更清晰

image
\(变化后是这样,5是新的点,5的权值为1\)
image

  1. 我们需要搞出来树根 \(root\) 代码里最后有一行 \(root=n+tot\) 为什么这么做呢?他重构树之后显然是有 \(森林\) 的,但他最后一个点不可能是森林(不然他无父无母的都遍历不到这个点),相当重要的,否则只有15分,后面 \(dfs\) 时有用
  2. 某个脑残问题,看到那个 \(break\) 没有,改成 \(return\)\(root\) 就没了 \(\large{qwq}\)

然后快乐的打完 \(lca\) 就完事了,最后输出两点最近公共祖先的权值即可,题中有一个若两点无法到达,输出 \(-1\) ,判断一下他们两个是不是在一个并查集里,如果不是自然就是不通

并查集如果用我这种方法别忘了 \(初始化\),初始化时要搞到 \(n+m\) ,因为我们是要建新的点的不然会炸内存

代码呈上

#include<bits/stdc++.h>
#define endl '\n'
#define int long long 
using namespace std;
const int N=5e5+1;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
int n,m,q,cnt,root,tot;
int w[N],f[N],fa[N],dep[N],sz[N],son[N],top[N];
vector<int>g[N];
struct aa{int x,y,w;}e[N];
bool cmp(aa s1,aa s2){return s1.w>s2.w;}
int find(int x)
{
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
void kru()
{
    stable_sort(e+1,e+1+m,cmp);
    cnt=n;
    for(int i=1;i<=m;i++)
    {
        int a=find(e[i].x),b=find(e[i].y);
        if(a==b) continue;
        f[a]=f[b]=++cnt;
        g[a].push_back(cnt),g[cnt].push_back(a),
        g[b].push_back(cnt),g[cnt].push_back(b);
        w[cnt]=e[i].w;
        if(++tot==n-1) break;
    }
    root=n+tot;
}
void dfs1(int x,int t)
{
    fa[x]=t,dep[x]=dep[t]+1,sz[x]=1;
    for(int y:g[x])
        if(y!=t)
        {
            dfs1(y,x);
            sz[x]+=sz[y];
            if(sz[son[x]]<sz[y]) son[x]=y;
        }
}
void dfs2(int x,int t)
{
    top[x]=t;
    if(!son[x]) return ;
    dfs2(son[x],t);
    for(int y:g[x])
        if(y!=son[x]&&y!=fa[x])
            dfs2(y,y);
}
int lca(int x,int y)
{
    for(;top[x]!=top[y];x=fa[top[x]])
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
    return dep[x]<dep[y]?x:y;
}
signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    int x,y;
    read(n),read(m);
    for(int i=1;i<=n+m;i++) f[i]=i;
    for(int i=1;i<=m;i++)
        read(e[i].x),read(e[i].y),read(e[i].w);
    kru(),dfs1(root,0),dfs2(root,root);
    read(q);
    while(q--)
        read(x),read(y),
        cout<<((find(x)!=find(y))?-1:w[lca(x,y)])<<endl;
}

并查集还可以这么打(虽然大多数人都是这么打的)

int find(int x)
{
    return (!f[x])?x:f[x]=find(f[x]);
}

这样就不需要初始化了

posted @ 2023-12-18 21:44  卡布叻_周深  阅读(17)  评论(0编辑  收藏  举报