首页 > 其他分享 >最短路

最短路

时间:2024-08-23 15:52:57浏览次数:12  
标签:原图 int 短路 方点 圆方树 非头点 Dis

这是仙人掌的模板题,仙人掌不能有自环,但是可以有重边。多颗仙人掌组成的图叫做沙漠。将仙人掌的每个环缩成一个点之后,就会形成树

仙人掌转树要利用圆方树:

①.任选一个点为根
②.此时每个环有且仅有唯一一个点到根的距离最近。然后将环中的点分类,离根节点最近的点叫“头”,剩余的点作为一类。接下来要将环变形。对于一个环,新建立一个方点,然后从头向方点连边(权值为\(0\)),方点向环上剩余的点连边(设某个剩余的点为\(x\),权值为“头”到\(x\)的最短距离),于是就将一个环拆成了一棵树,如下

image

image

③.分类讨论。一个自然的想法就是求\(x,y\)在圆方树上的最近公共祖先\(p\),然后在树上求两者的最短距离。如果说\(p\)是圆点的话是成立的(此时\(x,y\)要么不在一个环里且\(x,y\)之间的(在原图的)路径必经过\(p\)或者\(x,y\)在一个环里且\(x\)或\(y\)就是\(p\));如果\(p\)是方点的话,就说明\(x,y\)在原图的路径会先走到同一个环上的两个非头点(因为圆方树中\(p\)为方点,就说明从两个不是交汇环的非头点走到\(p\)),然后再在这个环上从一个非头点走到另一个非头点。前者可以用\(p\)是圆点的思路求,后者求出环上两条路径取更小值就好了。举个例子

image

然后对应到圆方树上的\(x,y\)就清楚了

以上就是仙人掌转圆方树的三个步骤,都是这么做的

实现细节:

一.原图当中一个环就是一个v-DCC

二.找头的时候,就是从根节点直接遍历,遍历到的第一个点就是头

上面的写法是狭义圆方树,其实更一般的写法是广义圆方树,见OI-wiki;形象的图的话其实可以这么看:先将原图按照tarjan缩点成v-DCC,就长成了蓝书上面的那个图,然后圆方树相当于不让割点单独成点,然后用方点连接一个v-DCC的所有点,割点连接不同的v-DCC;做题步骤还是一样的,但是广义圆方树由于一条边连接的一定是一个圆点和一个方点,更好分类讨论。这道题目具体来说:定义一个环的头是\(\text{dfn}\)最小的点(形象来看,也就是蓝书上面那个缩点之后的图深度最小的割点),方点与头连的边权为\(0\),与某个剩余点\(x\)连的边权为\(x\)到头的最短距离;询问\(x,y\)的时候,先找到两者在圆方树上的最近公共祖先\(p\),如果\(p\)是圆点,那么\(x,y\)在原图的最短距离就是圆方树上两者的距离,如果\(p\)是方点,那么\(x,y\)在原图上的路线就会先走到方点所代表的环(也就是一个v-DCC)的两个非头点,然后我们计算这两个非头点的最短距离就好了,具体见下面的代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e4+10,M=2e4+10;
int n,m,q;
int End[2][M<<2],Next[2][M<<2],Last[2][N<<1],Len[2][M<<2],cnt[2];
int dfn[N<<1],visitime,low[N<<1],val[N],dis[N],Dis[N<<1],ringlen[N<<1];
int tot,tp,st[N<<1];
int deep[N<<1];
int f[N<<1][30];
void add(int x,int y,int d,bool op)
{
	End[op][++cnt[op]]=y,Next[op][cnt[op]]=Last[op][x],Len[op][cnt[op]]=d,Last[op][x]=cnt[op];
}
void tarjan(int x,int lastedge)
//这里要像找桥边一样判断重边
//因为仙人掌其实是可以有重边的
//比如两个点之间有两条边,仍然不与仙人掌的定义矛盾 
{
    dfn[x]=low[x]=++visitime;
    st[++tp]=x;
    for(int i=Last[0][x];i;i=Next[0][i])
    {
        if((i^1)==lastedge) continue;
        int v=End[0][i];
        if(!dfn[v])
        {
            val[v]=Len[0][i];//val是环上最后一条边的权值 
            dis[v]=dis[x]+Len[0][i];//dis是搜索树上节点到根的距离 
            tarjan(v,i);
            low[x]=min(low[x],low[v]);
            if(dfn[x]<=low[v])//此时找到一个v-DCC 
            {
                tot++;//增加一个方点 
                ringlen[tot]=val[st[tp]]+dis[st[tp]]-dis[x];
                //注意此时栈中节点的排列顺序的意义 
				//从栈顶到x就是这个环的所有点
				//而且是按照顺序的
				//也就是说环就为st[tp]-st[tp-1]-...-x-st[tp] 
				//于是可以计算出环的长度 
                int z;
                do
                {
                    z=st[tp--];
                    int temp=min(ringlen[tot]-(dis[z]-dis[x]),dis[z]-dis[x]);
                    //点到头的最短距离 
                    add(tot,z,temp,1),add(z,tot,temp,1);
                }while(z!=v);
                add(x,tot,0,1),add(tot,x,0,1);
                //别忘了头和方点之间也要连边 
            }
        }
        else if(dfn[v]<dfn[x])
		//如果没有自环,可以直接else
		//有自环的话,要加上if(dfn[v]<dfn[x])
		//此时访问的是一条非树边的返祖边,肯定也就找到了环
		//所以要更新val,因为是环的最后一条边 
        {
            low[x]=min(low[x],dfn[v]);
            val[x]=Len[0][i];
        }
    }
}
void deal_first(int u,int father)
{
    deep[u]=deep[father]+1;
    for(int i=0;i<=15;i++)
    f[u][i+1]=f[f[u][i]][i];
    for(int i=Last[1][u];i;i=Next[1][i])
    {
        if(End[1][i]!=father)
        {
            f[End[1][i]][0]=u;
            Dis[End[1][i]]=Dis[u]+Len[1][i];
            deal_first(End[1][i],u);
        }
    }
}
pair<int,int> LCA(int x,int y)//second为-1表示lca为圆点 
{
    if(x==y) return make_pair(x,-1);//很重要
    if(deep[x]<deep[y]) swap(x,y);
    for(int i=15;i>=0;i--)
    {
        if(deep[f[x][i]]>=deep[y])
        x=f[x][i];
        if(x==y) return make_pair(x,-1);
        if(deep[x]==deep[y]) break;
    }
    for(int i=15;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    if(f[x][0]<=n) return make_pair(f[x][0],-1);
    else return make_pair(x,y);//注意返回的是x和y不是两者的父亲 
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	tot=n;//tot表示圆方树的节点总个数 
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w,0),add(v,u,w,0);
		//0表示原图的边,1表示圆方树的边 
	}
	tarjan(1,0);//这道题目没有孤立点,所以许多写法都简化了一下
	deal_first(1,0);//预处理圆方树的LCA 
	while(q--)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		pair<int,int> p=LCA(u,v);
		if(p.second==-1) printf("%d\n",Dis[u]+Dis[v]-2*Dis[p.first]);
		else
		{
			int temp=min(ringlen[f[p.first][0]]-abs(dis[p.first]-dis[p.second]),abs(dis[p.first]-dis[p.second]));
			printf("%d\n",Dis[u]-Dis[p.first]+Dis[v]-Dis[p.second]+temp);
		}
	}
    return 0;
}

标签:原图,int,短路,方点,圆方树,非头点,Dis
From: https://www.cnblogs.com/dingxingdi/p/18376110

相关文章

  • 最短路 - Dijkstra 算法
    Dijkstra(迪杰斯特拉)算法是基于贪心思想的单源最短路算法暴力Dijkstra具体如下:structnode{ intv,w;};vector<node>e[N];intdist[N],vis[N];e[u]存的是节点u的所有出边的终点和边权,dist[u]存u到原点s的最小距离,vis[u]标记是否走过voiddijkstra(int......
  • 《数据结构》最短路径Dijkstra算法
                                    最短路径Dijkstra算法分析生长点ABCDEFP(A)=FAD(A)=130P(B)=FBD(B)=24P(C)=FCD(C)=10P(D)=——D(D)=无穷P(E)=——D(E)=无穷CP(A)=FAD(A......
  • 最短路算法
    1.Dijkstra算法算法思想:\(dijkstra\)算法采用的是贪心的思想。(1)定义一个\(dis\)数组,\(dis[i]\)表示i点到源点的最短路径,设源点的\(dis\)值为0,其他\(dis\)值为\(∞\)。(2)选出其中的最小\(dis\)值,进行标记并更新它相邻的\(dis\)值。(3)不断循环操作(2)。优点:dijkstra算......
  • Java中的图算法:如何实现高效的最短路径计算
    Java中的图算法:如何实现高效的最短路径计算大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!作为开头。最短路径算法是图论中的一个核心问题,广泛应用于网络路由、地图导航等领域。在Java中实现高效的最短路径计算通常涉及到Dijkstra算法和Floy......
  • 最短路(DJsktra,spfa,flyd).md
    最短路弗洛伊德:全源最短路:\[\LargeDP方程:\\dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])\]#include<cstdio>#include<algorithm>#include<iostream>#include<cstring>#defineintlonglong#defineiosstd::ios::sync_with_stdio(false);s......
  • QOJ #8673. 最短路径
    题面传送门一年前,折戟沉沙,后面忘了。首先我们考虑折半搜索去做这个题。对于\(x\),在正向的图上跑Dijkstra,对于\(y\),在反图上跑Dijkstra。当两边搜到同一个点的时候,所有的最短路都可以表示成:\(x\tox'\toy'\toy\),其中\(x'\)是\(x\)已经扩展过的点,\(y'\)是\(y\)已经扩......
  • Johnson全源最短路
    Johnson全源最短路引入对于一个无负环的图,我们可以用Floyd或n遍Bellman-ford求出它的全源最短路Floyd复杂度为O(\(n^3\))在稀疏图上效率极低n遍Bellman-fordO(\(n^2m\))效率远不及Floyd注意到n遍dijstra复杂度为O(\(nm~log~m\))或O(\(n^3\))快于Floyd但无法在负权图上跑,考......
  • 最短路算法
    存在最短路的前提不存在负环。链接还是OIWiki好啊~~Floyd算法两两间最短路,可判负环。时间复杂度\(O(n^3)\)。像DP的思路一样。设\(f_{k,x,y}\)表示允许经过\(1\simk\)的点,\(x\toy\)的最短距离。\(O(n^3)\)的DP即可。按照\(k,x,y\)的顺序循环,每次与\(......
  • 「Day 5—最短路径」
    最短路问题单源最短路全源最短路Floyd算法通过转移方程判断i->j的路径中,是否有i->k->j更短,运用简单dp来转移状态。f[i][j]表示i->j的最短路径长度。但不要忘了初始化,一个点到其本身的最短路径为1,即f[i][i]=1,其余的初始化为'1e9'即可。for(inti=......
  • 【学习笔记】Matlab和python双语言的学习(图论最短路径)
    文章目录前言一、图论基本概念示例二、代码实现----Matlab三、代码实现----python总结前言通过模型算法,熟练对Matlab和python的应用。学习视频链接:https://www.bilibili.com/video/BV1EK41187QF?p=36&vd_source=67471d3a1b4f517b7a7964093e62f7e6一、图论图论(G......