首页 > 其他分享 >最短路计数

最短路计数

时间:2023-12-24 09:23:17浏览次数:42  
标签:cnt int 短路 pos 计数 cnt2 dis

前置知识

  • 最短路的一个很好的性质:从\(s\)到\(t\)的最短路上的一个节点\(k\),都满足\(s\)到\(k\)的路径是关于\(s\)单源最短路的最短路

证明:

反证法,假设\(s\)到\(k\)的路径不为最短路,但\(s \to k \to t\)为到\(t\)的最短路,那么\(s \to k \to t\)的路径一定不会比\(s\)到\(k\)的最短路再加上\(k \to t\)的路径优,所以\(s \to t\)就不为最短路,与上文假设条件矛盾。

故得证。

  • 在\(s\)到\(t\)的最短路径上一定不存在环

证明:

考虑反证法,因为这个图的边权非负,所以环的权值为非负数,所以一定不会比不经过这个环更优。

故得证。

注: 上述结论是在图的边权非负时才成立的,如果图的边权为负数上述结论就不一定成立了


例题[HAOI2012] 道路

直接队每一条边进行枚举肯定会\(TLE\),但如果只计算边的贡献就好像可以优化一点时间复杂度。

设\(cnt1_v\)表示从\(s\)到达\(v\)的最短路径条数,\(cnt2_v\)表示在最短路图上以\(v\)作为起点的最短路径条数。

很显然,对于一条边\(u \to v\),它的贡献就为以\(1 \sim n\)每个点作为起点\(cnt1_u \times cnt2_v\)的和。

\(cnt1\)可以在\(dijkstra\)后进行判断哪些边在最短路图上再进行\(topo\)求得,显然\(cnt1_s=1\)。

\(cnt2\)可以在拓扑序的逆序上求得,对于一个点\(u\),\(cnt2_u=1+cnt2_v\),其中\(v\)为\(u\)的相邻节点。

代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=1550,M=5050;
const int mod=1e9+7;
int n,m,head[N],cnt,to[M],nxt[M],w[M],dis[N],fro[M],in[N],cnt1[N],cnt2[N],s[N],ans[M];
bool vis[N],mark[M];
void add(int u,int v,int f)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
	w[cnt]=f;
	fro[cnt]=u;
}
struct node{
	int val,pos;
	bool operator >(const node &x)const{
		return val>x.val;
	}
};
void dij(int s)
{
	priority_queue<node,vector<node>,greater<node> >q;
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(mark,0,sizeof(mark));
	dis[s]=0;
	q.push(node{dis[s],s});
	while(!q.empty())
	{
		node t=q.top();q.pop();
		if(vis[t.pos])continue;
		vis[t.pos]=1;
		for(int i=head[t.pos];i;i=nxt[i])
		{
			int v=to[i];
			if(dis[v]>dis[t.pos]+w[i])
			{
				dis[v]=dis[t.pos]+w[i];
				q.push(node{dis[v],v});
			}
		}
	}
	for(int i=1;i<=m;i++)
		if(dis[to[i]]==dis[fro[i]]+w[i])mark[i]=true;
	return;
}
void topo(int fs)
{
	memset(cnt1,0,sizeof(cnt1));
	memset(cnt2,0,sizeof(cnt2));
	memset(in,0,sizeof(in));
	queue<int>q;
	for(int i=1;i<=m;i++)
	{
		if(mark[i]==false)continue;
		in[to[i]]++;
	}
	q.push(fs);
	cnt1[fs]=1;
	int tag=0;
	while(!q.empty())
	{
		int x=q.front();q.pop();
		s[++tag]=x;
		for(int i=head[x];i;i=nxt[i])
		{
			if(mark[i]==false)continue;
			in[to[i]]--;
			if(in[to[i]]==0)q.push(to[i]);
			cnt1[to[i]]+=cnt1[x];
			cnt1[to[i]]%=mod;
		}
	}
	for(int i=tag;i;i--)
	{
		int x=s[i];
		cnt2[x]++;
		for(int j=head[x];j;j=nxt[j])
		{
			if(mark[j]==false)continue;
			cnt2[x]+=cnt2[to[j]];
			cnt2[x]%=mod;
		}
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1,u,v,w;i<=m;i++)
	{
		scanf("%d %d %d",&u,&v,&w);
		add(u,v,w);
	}
	for(int i=1;i<=n;i++)
	{
		dij(i);topo(i);
		for(int i=1;i<=m;i++)
		{
//		cout<<cnt1[fro[i]]<<" "<<cnt2[to[i]]<<endl;
			if(mark[i])
			{
				ans[i]+=(cnt1[fro[i]]%mod*cnt2[to[i]]%mod)%mod;
				ans[i]%=mod;
			}
		}
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

时间复杂度\(O(nmlogm)\)

标签:cnt,int,短路,pos,计数,cnt2,dis
From: https://www.cnblogs.com/CQWYB/p/17924029.html

相关文章

  • 7-5 单源最短路径
    7-5单源最短路径请编写程序求给定正权有向图的单源最短路径长度。图中包含n个顶点,编号为0至n-1,以顶点0作为源点。输入格式:输入第一行为两个正整数n和e,分别表示图的顶点数和边数,其中n不超过20000,e不超过1000。接下来e行表示每条边的信息,每行为3个非负整数a、b、c,其中a和b表示......
  • 图论——最短路
    https://csacademy.com/app/graph_editor/https://riverhamster.gitee.io/app/graph_editor/注:时间复杂度分析中,假设\(n\lem\len^2\)最短路本质上是一种DP阶段:点状态:拆点决策:边最优子结构:最短路的任何子路径都一定是最短路无后效性:正权图中一定可以找到一种无后......
  • 使用OCCT构建两个面之间的最短路径
    查找两个面之间的最短面路径查找面的邻面。std::vector<TopoDS_Face>OCCTUtility::adjacentFace(TopoDS_Faceconst&face,std::optional<TopoDS_Shape>shape,std::optional<TopTools_IndexedDataMapOfShapeListOfShape>edgeMapFace){std::vector<TopoD......
  • 【模版】计数排序
    引入:P1271【深基9.例1】选举学生会在实际中,一般会在投票区放n个投票箱,投完后只需要计数每个投票箱即可。就此可引入计数排序。本题AC代码(虽然这题直接sort就行了...)#include<iostream>usingnamespacestd;inta[1010]={0},n,m,tmp;intmain(){cin>>n>>m;for......
  • dijkstra最短路
    给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。输入格式第一行包含整数 n 和 m。接下来 m行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y的......
  • JVM虚拟机系统性学习-运行时数据区(方法区、程序计数器、直接内存)
    方法区方法区本质上是Java编译后代码的存储区域,存储了每一个类的结构信息,如:运行时常量池、成员变量、方法、构造方法和普通方法的字节码指令等内容方法区主要存储的数据如下:Class类型信息,如该Class为class类、接口、枚举、注解,类的修饰符等等信息方法信息(方法名称、方法返回......
  • 程序计数器
    一、概述程序计数器(ProgramCounterRegister)是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器,在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器;分支、循环、跳转......
  • Python算法——计数排序
    计数排序(CountingSort)是一种非比较性排序算法,适用于对一定范围内的整数进行排序。它通过统计每个元素出现的次数,然后根据统计信息重新构建有序数组。计数排序是一种线性时间复杂度的排序算法,具有稳定性和适用性广泛的特点。本文将详细介绍计数排序的工作原理和Python实现。计数排......
  • 【洛谷 P1271】【深基9.例1】选举学生会 题解(计数排序)
    【深基9.例1】选举学生会题目描述学校正在选举学生会成员,有名候选人,每名候选人编号分别从1到,现在收集到了张选票,每张选票都写了一个候选人编号。现在想把这些堆积如山的选票按照投票数字从小到大排序。输入格式输入和以及个选票上的数字。输出格式求出排序后的选票编......
  • 12.11 迪杰斯特拉方法实现最短路径(c++)
     今天通过自主学习,,对数据结构中的迪杰斯特拉方法实现最短路径进行了深造,让我学会了很多新的东西。首先是问题描述:用迪杰斯特拉算法实现有向网的最短路径输入格式:第一行输入有向网的顶点和边数,第二行输入各顶点值,用空格间隔,第三行开始输入各条边的两个点的及边上的权值,用空......