最短路径生成树

前言

为班刊而写

首先,这个东西是一个非常冷门的知识点,模板题好像就是一道紫题(省选),但害怕考上了,于是,找了少有的博客,整理一下

定义

我们知道最小生成树是一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出,俗话讲,就是求一个图,削成树后,树上留下边权和最小

那么,最短路径生成树是什么?

最短路径生成树,就是ROOT根节点到达任意点距离最短的路径所构成的树,就是最短路径生成树

也就是说,最短路径树是一个根据根节点形成的每一个点到根节点距离最小的树

知道了定义,那么,最短路径树有何应用?----导航

求法

实际上,看到定义就知道,可以用,\(dijkstra\)来求最短路(这个不会的下面的也看不懂),至于路径,就是在计算时记录路,然后,在最后记下路径就行了

实际上最短路径生成树最难的是记数,统计一共多少棵树

这个实际上也是基于\(dijkstra\),在每次松弛时,对于如果相等时,就可以用一个数组记录从原点到当前点最短的路径的方案,最后,可以用乘法原理,统计出最后的答案

看到这,实际上,最短路径树是一个统计最短路径的形式,甚至不是一个算法,所以,题目一般都是变形过后的题目

[HAOI2012]道路

题目描述

C 国有 \(n\) 座城市,城市之间通过 \(m\)单向道路连接。一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小。两条最短路不同,当且仅当它们包含的道路序列不同。我们需要对每条道路的重要性进行评估,评估方式为计算有多少条不同的最短路经过该道路。现在,这个任务交给了你。

输入格式

第一行包含两个正整数 \(n, m\)

接下来 \(m\) 行每行包含三个正整数 \(u, v, w\),表示有一条从 \(u\)\(v\) 长度为 \(w\) 的道路

输出格式

输出应有 \(m\) 行,第 \(i\) 行包含一个数,代表经过第 \(i\) 条道路的最短路的数目对 \(10^9+7\) 取模后的结果。

样例 #1

样例输入 #1

4 4
1 2 5
2 3 5
3 4 5
1 4 8

样例输出 #1

2
3
2
1

提示

数据规模

\(30\%\) 的数据满足:\(n\leq 15, m\leq 30\)

\(60\%\) 的数据满足:\(n\leq 300, m\leq 1000\)

\(100\%\) 的数据满足:\(n\leq 1500, m\leq 5000, w\leq 10000\)

题目分析

首先对于这道题,可以对每一个点为根建一棵最小路径树,然后,树上的边实际上就是一个最短路径的一条边,于是,就可以对此贡献,然后最后,用乘法原理统计答案即可
(说白了,这没用)
其实我在口胡。。。

这个是有向图生成的是\(DAG\)树。。。。

方案统计还是比较简单吧

\(S\)为起点\(Dij\),跑正反两次\(Top\),分别统计\(S\)\(U\),\(V\)到任意点的方案数

#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=1505;
int n,m;
struct Edge{
	int ind,v,val;
};
vector<Edge>g[MAXN];
pair<int,int>edge[5005];
int x,y,z;
int vis[MAXN];
int dp[MAXN];
struct node{
	int u,val;
	bool operator<(const node x)const{
		return val>x.val;
	}
};
vector<pair<int,int> >Pre_node[MAXN];
void dijkstra(int s)
{
	memset(vis,0,sizeof(vis));
	memset(dp,0x3f,sizeof(dp));
	dp[s]=0;
	priority_queue<node>q;
	node Nxc;
	Nxc.u=s;
	Nxc.val=0;
	q.push(Nxc);
	while(q.size())
	{
		node temp=q.top();
		q.pop();
		if(vis[temp.u])
		{
			continue;
		}
		vis[temp.u]=1;
		for(int i=0;i<g[temp.u].size();i++)
		{
			int v=g[temp.u][i].v;
			int w=g[temp.u][i].val;
			int Ind=g[temp.u][i].ind;
			if(dp[v]>dp[temp.u]+w)
			{
				dp[v]=dp[temp.u]+w;
				node sfccc;
				sfccc.u=v;
				sfccc.val=dp[v];
				q.push(sfccc);
			//	printf("%d--%da \n",v,dp[v]);
				Pre_node[v].clear();
				Pre_node[v].push_back(make_pair(temp.u,Ind));
			}
			else if(dp[v]==dp[temp.u]+w)
			{
				Pre_node[v].push_back(make_pair(temp.u,Ind));
			}
		}
	}
}
int Ans[5005];
vector<int>G[MAXN];
int rd[MAXN];
int Approach[MAXN];
int Rev_approach[MAXN];
int Res=0;
int Can[5005];
void solve(int s)
{
	for(int i=1;i<=n;i++)
	{
		Pre_node[i].clear();
		G[i].clear();
		rd[i]=0;
		
	}
	for(int i=1;i<=m;i++)
	{
		Can[i]=0;
	}
	dijkstra(s);
	//printf("Fuck:::%d\n",Pre_node[3].size());
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<Pre_node[i].size();j++)
		{
			int vxc=Pre_node[i][j].first;
			rd[i]++;
			G[vxc].push_back(i);
			Can[Pre_node[i][j].second]=1;
		}
		Approach[i]=0;
		Rev_approach[i]=0;
	}
	queue<int>q;
	for(int i=1;i<=n;i++)
	{
		if(!rd[i])
		{
			Approach[i]=1;
			q.push(i);
		}
	}
	while(q.size())
	{
		int temp=q.front();
		q.pop();
		for(int i=0;i<G[temp].size();i++)
		{
			int v=G[temp][i];
			Approach[v]+=Approach[temp];
			rd[v]--;
			if(!rd[v])
			{
				q.push(v);
			}
		}
	}
//	printf("Fuck:::%d\n",Pre_node[3].size());

	for(int i=1;i<=n;i++)
	{
		G[i].clear();
		rd[i]=0;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<Pre_node[i].size();j++)
		{
			int vxc=Pre_node[i][j].first;
			rd[vxc]++;
			G[i].push_back(vxc);	
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(!rd[i])
		{
			q.push(i);
		//	printf("%d???????\n",i);
		}	
	}
	while(q.size())
	{
		int temp=q.front();
		q.pop();
		Rev_approach[temp]++;
	//	printf("%d %d?\n",temp,Rev_approach[temp]);
		for(int i=0;i<G[temp].size();i++)
		{
			int v=G[temp][i];
			Rev_approach[v]+=Rev_approach[temp];
			rd[v]--;
			if(!rd[v])
			{
				q.push(v);
			}
		}
	}
//	printf("%d?\n",Pre_node[2].size());
//	printf("%d\n",Pre_node[2][0]);
//	printf("%d????\n",Available[1][2]);
	for(int i=1;i<=m;i++)
	{
		int U=edge[i].first;
		int V=edge[i].second;
		if(!Can[i])
		{
			continue;
		}
	//	printf("%d %d---\n",U,V);
		
		Ans[i]=((long long)Ans[i]+((long long)Approach[U]*Rev_approach[V])%MOD)%MOD;
	//	printf("%d %d %d %d\n",s,i,Approach[U],Rev_approach[V]);
	}
}

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		Edge Nox;
		Nox.v=y;
		Nox.val=z;
		Nox.ind=i;
		g[x].push_back(Nox);
		edge[i].first=x;
		edge[i].second=y;
	}
	for(int i=1;i<=n;i++)
	{
		solve(i);
	}
	for(int i=1;i<=m;i++)
	{
		printf("%d\n",Ans[i]);
	}
}

补充

感觉以前写的好敷衍亚

例题其实不是最短路径生成树

给一道最短路径生成树的题吧。。。

[FJOI2014]最短路径树问题

题目描述

给一个包含 \(n\) 个点,\(m\) 条边的无向连通图。从顶点 \(1\) 出发,往其余所有点分别走一次并返回。

往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径 A 为 \(1,32,11\),路径 B 为 \(1,3,2,11\),路径 B 字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。

可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含 \(K\) 个点的简单路径长度为多长?包含 \(K\) 个点的长度为该最长长度的不同路径有多少条?

这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点 A 到点 B 的路径和点 B 到点 A 视为同一条路径。

输入格式

第一行输入三个正整数 \(n,m,K\),表示有 \(n\) 个点 \(m\) 条边,要求的路径需要经过 \(K\) 个点。

接下来输入 \(m\) 行,每行三个正整数 \(A_i,B_i,C_i(1\leq Ai,Bi\leq n,1\leq C_i \leq 10000)\),表示 \(A_i\)\(B_i\) 间有一条长度为 \(C_i\) 的边。

数据保证输入的是连通的无向图。

输出格式

输出一行两个整数,以一个空格隔开,第一个整数表示包含 \(K\) 个点的路径最长为多长,第二个整数表示包含 \(K\) 个点的长度为该最长长度的不同路径有多少条。

样例 #1

样例输入 #1

6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1

样例输出 #1

3 4

提示

对于所有数据 \(n\leq 30000,m\leq 60000,2\leq K\leq n\)

数据保证最短路径树上至少存在一条长度为 \(K\) 的路径。

其实就是缝合怪???

建一棵最短路径生成树然后貌似就是点分治板子

主要是如何保证字典序最小

可以在\(Dij\)的过程中记录可以转移的边,保留这些边

然后按编号排序,在\(dfs\)一下就可以了(模拟赛才考就忘了。。。。。)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
int n,m;
int k;
int Sum;
int Siz[MAXN];
int Heart;
int Minh;
struct Edge{
	int v,val;
};
vector<Edge>g[MAXN];

int x,y,z;
int Vis[MAXN];
void Find_Heart(int x,int f)
{
	int Maxs=0;
	Siz[x]=1;
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i].v;
		int w=g[x][i].val;
		if(v==f||Vis[v])
		{
			continue;
		}
		Find_Heart(v,x);
		Maxs=max(Maxs,Siz[v]);
		Siz[x]+=Siz[v];
	}
	Maxs=max(Maxs,Sum-Siz[x]);
	if(Maxs<Minh)
	{
		Minh=Maxs;
		Heart=x;
	} 
}
int Ans=0;
int Tot=0;
int Cinit[MAXN];
int cnt_init;
int C[MAXN];
int Ct[MAXN];
int dis[MAXN];
int dep[MAXN];
int Rec[MAXN];
int cnt_rec;
void Get_Dis(int x,int f)
{
	Rec[++cnt_rec]=x;
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i].v;
		int w=g[x][i].val;
		if(v==f||Vis[v])
		{
			continue;
		}
		dis[v]=dis[x]+w;
		dep[v]=dep[x]+1;
		Get_Dis(v,x);
	}
}
void cal(int x)
{
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i].v;
		int w=g[x][i].val;
		if(Vis[v])
		{
			continue;
		 } 
		 dis[v]=w;
		 dep[v]=1;
		 cnt_rec=0;
		 Get_Dis(v,0);
		 for(int j=1;j<=cnt_rec;j++)
		 {
		 	int Key=Rec[j];
		//	 printf("%d %d %d?\n",Key,dep[Key],dis[Key]);
		 	if(dep[Key]<=k)
		 	{
		 		int Rest=k-(dep[Key]);
		 		if(Ct[Rest])
		 		{
		 		
		 			if(C[Rest]+dis[Key]>Ans)
		 			{
		 				Ans=C[Rest]+dis[Key];
		 				Tot=Ct[Rest];
					 }
					 else if(C[Rest]+dis[Key]==Ans)
					 {
					 	Tot+=Ct[Rest];
					 }
				 }
			 }
		 }
		 for(int j=1;j<=cnt_rec;j++)
		 {
		 	int Key=Rec[j];
		 	int GEX=dep[Key]+1;
		 	if(GEX<=k)
		 	{
				Cinit[++cnt_init]=GEX;
			 	if(C[GEX]<dis[Key])
			 	{
			 		C[GEX]=dis[Key];
			 		Ct[GEX]=1;
				 }
				 else if(C[GEX]==dis[Key])
				 {
				 	Ct[GEX]++;
				 }
			 }
		 
		 }
	}
	for(int i=1;i<=cnt_init;i++)
	{
		C[Cinit[i]]=0;
		Ct[Cinit[i]]=0;
	}
}
void solve(int x)
{
	Vis[x]=1;
	cnt_init=0;
	Cinit[++cnt_init]=1;
	C[1]=0;
	Ct[1]=1;
	cal(x);
//	printf("%d %d\n",Ans,Tot);
	//printf("%d??\n",x);
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i].v;
		if(Vis[v])
		{
			continue;
		}
		Sum=Siz[v];
		Minh=0x3f3f3f3f;
		Find_Heart(v,0);
		Find_Heart(Heart,0);
		solve(Heart);	
	}	
}
vector<Edge>G[MAXN];
struct node{
	int u,val;
	bool operator<(const node x)const{
		return val>x.val;
	}
};
int vis[MAXN];
int dp[MAXN];
vector<Edge>Pre_node[MAXN];
vector<Edge>gg[MAXN];
void dijkstra(int s)
{
	priority_queue<node>q;
	memset(vis,0,sizeof(vis));
	memset(dp,0x3f,sizeof(dp));
	dp[s]=0;
	node wdx;
	wdx.u=s;
	wdx.val=0;
	q.push(wdx);
	while(q.size())
	{
		node temp=q.top();
		q.pop();
		for(int i=0;i<G[temp.u].size();i++)
		{
			int v=G[temp.u][i].v;
			int w=G[temp.u][i].val;
			if(dp[v]>dp[temp.u]+w)
			{
				dp[v]=dp[temp.u]+w;
				Pre_node[v].clear();
				Edge Dxv;
				Dxv.v=temp.u;
				Dxv.val=w;
				Pre_node[v].push_back(Dxv);
				node sx;
				sx.u=v;
				sx.val=dp[v];
				q.push(sx);
			}
			else if(dp[v]==dp[temp.u]+w)
			{
				Edge Dxv;
				Dxv.v=temp.u;
				Dxv.val=w;
				Pre_node[v].push_back(Dxv);
			}
		 } 
	}
}
bool cmp(Edge x,Edge y)
{
	return x.v<y.v;
 } 
int VVV[MAXN];
void dfs(int x,int f)
{
	VVV[x]=1;
	for(int i=0;i<gg[x].size();i++)
	{
		int v=gg[x][i].v;
		int w=gg[x][i].val;
		if(v==f){
			continue;
		}
		if(VVV[v])
		{
			continue;
		}
		Edge NQ520;
		NQ520.v=v;
		NQ520.val=w;
		g[x].push_back(NQ520);
		NQ520.v=x;
		g[v].push_back(NQ520);
		dfs(v,x);
	}
}
int main()
{
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		Edge Dx;
		Dx.v=y;
		Dx.val=z;
		G[x].push_back(Dx);
		Dx.v=x;
		G[y].push_back(Dx);
	}
	dijkstra(1);
	for(int i=1;i<=n;i++)
	{
		if(Pre_node[i].size())
		{
			for(int j=0;j<Pre_node[i].size();j++)
			{
				Edge Dxv=Pre_node[i][j];
				int To=Dxv.v;
				Dxv.v=i;
				gg[To].push_back(Dxv);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		sort(gg[i].begin(),gg[i].end(),cmp);
	}
	dfs(1,0);
//	for(int i=1;i<=n;i++)
//	{
//		for(int j=0;j<g[i].size();j++)
//		{
//			int v=g[i][j].v;
//			int w=g[i][j].val;
//			printf("%d %d %d\n",i,v,w);
//		}
//	}
	Sum=n;
	Minh=0x3f3f3f3f;
	Find_Heart(1,0);
	Find_Heart(Heart,0);
	solve(Heart);
	printf("%d %d",Ans,Tot); 
}
posted @ 2021-07-20 11:50  kid_magic  阅读(621)  评论(0编辑  收藏  举报