zoukankan      html  css  js  c++  java
  • bzoj2125 最短路——仙人掌两点间距离

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2125

    仙人掌!模仿 lyd 的代码写的,也算是努力理解了;

    主要分成 lca 在环上和不在环上,先缩环(环上的点直接连向最高点),那么不在环上的 lca 就跟在树上一样求法;

    在环上的话就先求出环外部分,再计算环内距离;

    所以一遍 spfa 求从根出发的最短路,再一遍 dfs 求 dfs 序的 dis ,用来处理环上距离,然后 bfs 计算深度用来倍增求 lca,然后分类求答案即可;

    注意边数就是点的4倍,还要算上缩环时连的边。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    using namespace std;
    int const maxn=10005,maxm=40005;//
    int n,m,Q,hd[maxn],ct=1,dis[maxn],dist[maxn],cr,col[maxn],tim,dfn[maxn];
    int fa[maxn],len[maxn],f[maxn][20],dep[maxn];
    bool del[maxm],vis[maxn];
    queue<int>q;
    struct N{
        int to,nxt,w;
        N(int t=0,int n=0,int w=0):to(t),nxt(n),w(w) {}
    }ed[maxm];
    void add(int x,int y,int z){ed[++ct]=N(y,hd[x],z); hd[x]=ct;}
    int ab(int x){return x<0?-x:x;}
    void spfa()
    {
        memset(dist,0x3f,sizeof dist);
        memset(vis,0,sizeof vis);
        dist[1]=0; q.push(1); vis[1]=1;
        while(q.size())
        {
            int x=q.front(); q.pop(); vis[x]=0;
            for(int i=hd[x],u;i;i=ed[i].nxt)
                if(dist[u=ed[i].to]>dist[x]+ed[i].w)
                {
                    dist[u]=dist[x]+ed[i].w;
                    if(!vis[u])vis[u]=1,q.push(u);
                }
        }
    }
    void make(int x,int e)
    {
        int i,y=x; x=ed[e].to;
        len[++cr]+=ed[e].w; col[y]=cr; del[e]=del[e^1]=1;
        add(x,y,0); add(y,x,0);//!连向最高点 
        for(i=fa[y];(y=ed[i^1].to)!=x;i=fa[y])
        {
            len[cr]+=ed[i].w; col[y]=cr;
            del[i]=del[i^1]=1;
            add(x,y,0); add(y,x,0);//!连向最高点 
        }
        col[x]=cr; len[cr]+=ed[i].w;
    }
    void dfs(int x)
    {
        dfn[x]=++tim;
        for(int i=hd[x],u;i;i=ed[i].nxt)
        {
            if(!dfn[u=ed[i].to])
            {
                fa[u]=i; dis[u]=dis[x]+ed[i].w;
                dfs(u);
            }
            else if(dfn[u]<dfn[x]&&fa[x]!=(i^1))make(x,i);
        }
    }
    void bfs()
    {
        while(q.size())q.pop();
        memset(vis,0,sizeof vis);
        vis[1]=1; q.push(1); dep[1]=1;
        while(q.size())
        {
            int x=q.front(); q.pop();
            for(int i=hd[x],u;i;i=ed[i].nxt)
            {
                if(vis[u=ed[i].to]||del[i])continue;
                vis[u]=1; dep[u]=dep[x]+1; f[u][0]=x;
                for(int j=1;j<=15;j++)f[u][j]=f[f[u][j-1]][j-1];
                q.push(u);
            }
        }
    }
    int lca(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        int a=x,b=y;
        int k=dep[x]-dep[y];
        for(int i=0;i<=15;i++)
            if(k&(1<<i))x=f[x][i];
        if(x==y)return dist[a]-dist[b];
        for(int i=15;i>=0;i--)
            if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
        if(col[x]&&col[x]==col[y])
        {
            int l=ab(dis[x]-dis[y]);
            return dist[a]-dist[x]+dist[b]-dist[y]+min(l,len[col[x]]-l); 
            //两个点从环外跳到环上,所以先加上环外部分的 dist,再算环上的最短距离 
        }
        return dist[a]+dist[b]-2*dist[f[x][0]];
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&Q);
        for(int i=1,x,y,z;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z); add(y,x,z);
        }
        spfa();
        dfs(1);
        bfs();
        for(int i=1,x,y;i<=Q;i++)
        {
            scanf("%d%d",&x,&y);
            printf("%d
    ",lca(x,y));
        }
    }
  • 相关阅读:
    北京清北 综合强化班 Day1
    Noip2015 提高组 Day1
    Noip2016 提高组 Day2 T1 组合数问题
    2017.9.23 NOIP2017 金秋杯系列模拟赛 day1 T1
    [51NOD1103] N的倍数(鸽笼原理)
    [51NOD1420] 数袋鼠好有趣(贪心)
    [CF808A] Lucky Year(规律)
    [CF808B] Average Sleep Time([强行]树状数组,数学)
    [CF808C] Tea Party(贪心)
    [CF808D] Array Division(暴力,枚举)
  • 原文地址:https://www.cnblogs.com/Zinn/p/9281793.html
Copyright © 2011-2022 走看看