zoukankan      html  css  js  c++  java
  • 图论复习P

    题意:n个点m条边,m-n≤20,给你q组询问,每组询问两个点之间的最短路径

    分析:如果是n个点,n-1条边,显然lca就能完美解决

    如果加一条边,显然需要把通过该边的路径与lca结果取最小值

    但如何求通过该边的路径的最小值呢?

    可以转化成通过该边两端点的路径的最小值

    而通过端点的路径的最小值可以以端点跑个spfa或dij,求出其到所有节点的距离

    那么从x到y经过点p的最短路径就可以表示为dis[p][x]+dis[p][y]

    当然,这里需要跑spfa的点最多只有42个,也就是在树的基础上加了42条边

    只需要将其取个最小值即可

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    #define ll long long
    
    const int maxm=2e5+1;
    const int maxp=3e1+1;
    const int maxn=1e5+1;
    const int maxlg=2e1+1;
    
    struct Node
    {
        int to,next,val;
    }e[maxm<<1];
    struct edge
    {
        int x,y,z;
    }a[maxm];
    int head[maxn];
    ll dis[maxp<<1][maxn];
    bool vis[maxn];
    int deep[maxn];
    bool isp_e[maxm];
    bool isp_d[maxn];
    int fa[maxn][maxlg];
    ll val[maxn][maxlg];
    int pp[maxp<<1];
    int cnt,n,m;
    
    void add(int x,int y,int z)
    {
        e[++cnt].val=z;
        e[cnt].to=y;
        e[cnt].next=head[x];
        head[x]=cnt;
    }
    
    void spfa(int x,int p)
    {
        deque<int> q;q.push_back(x),dis[p][x]=0ll;
        while(!q.empty())
        {
            int now=q.front();q.pop_front();
            vis[now]=0;
            for(int i=head[now];i;i=e[i].next)
            {
                int v=e[i].to;
                if(dis[p][v]>dis[p][now]+(ll)e[i].val)
                {
                    dis[p][v]=dis[p][now]+(ll)e[i].val;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        if(!q.empty()&&dis[p][v]<dis[p][q.front()]) q.push_front(v);
                        else q.push_back(v);
                    }
                }
            }
        }
    }
    
    void lca()
    {
        for(int i=1;i<maxlg;i++) for(int j=1;j<=n;j++)
        {
            int now=fa[j][i-1];
            fa[j][i]=fa[now][i-1];
            val[j][i]=val[j][i-1]+val[now][i-1];
        }
    }
    
    ll ask(int x,int y)
    {
        if(deep[x]<deep[y]) swap(x,y);
        int now=deep[x]-deep[y];
        ll ans=0;
        for(int i=0;now;i++,now>>=1) if(now&1) ans+=val[x][i],x=fa[x][i];
        if(x==y) return ans;
        for(int i=maxlg-1;i>=0;i--)
        {
            if(fa[x][i]!=fa[y][i])
            {
                ans+=val[x][i]+val[y][i];
                x=fa[x][i],y=fa[y][i];
            }
        }
        return ans+val[x][0]+val[y][0];
    }
    
    void dfs(int x,int fax)
    {
        vis[x]=1;
        for(int i=head[x];i;i=e[i].next)
        {
            int v=e[i].to;
            if(!vis[v])
            {
                fa[v][0]=x;
                val[v][0]=(ll)e[i].val;
                deep[v]=deep[x]+1;
                isp_e[i-1>>1]=1;
                dfs(v,x);
            }
        }
    }
    
    int main()
    {
        int q,x,y,z;
        memset(dis,0x3f,sizeof(dis));
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);add(y,x,z);
            a[i].x=x,a[i].y=y,a[i].z=z;
        }
        dfs(1,-1);
        memset(vis,0,sizeof(vis));
        for(int i=0;i<m;i++) if(!isp_e[i]) isp_d[a[i].x]=isp_d[a[i].y]=1;
        int pnum=0;
        for(int i=1;i<=n;i++) if(isp_d[i]) pp[++pnum]=i,spfa(i,pnum);
        lca();
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d%d",&x,&y);
            ll ans=ask(x,y);
            for(int i=1;i<=pnum;i++) ans=min(ans,dis[i][x]+dis[i][y]);
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    VSCode编写 Vue 项目标签内显示写CSS提示设置
    Vue 炫酷 Echarts 图表
    vue 动态生成拓扑图
    Vue 全局 websocket
    Vue 自定义组件v-model父子组件传值双向绑定
    vue项目Echarts更新数据是数据表展示错版
    Vue图片加载错误、图片加载失败的处理
    Vue 使用 Ant-d 简单实现左侧菜单栏和面包屑功能
    Vue Echarts图表dataZoom缩放区域根据数据量显示
    Echarts图例数据太多实现滚动效果
  • 原文地址:https://www.cnblogs.com/lin4xu/p/12856678.html
Copyright © 2011-2022 走看看