图论之路径条数专题

一直忙着金工实习+蓝桥杯,好久没有看图论了,今天就小试几题享受下被虐的快感。

1.最短路+拓扑

首先来几个结论:

1.最短路图没有环(可以用反证法证明)

2.dis[u]+edge[u,v]=dis[v],那么u,v端点的边一定在最短路图上。

因此,我们就可以先枚举起始点跑SPFA,然后把最短路找出来,假如一个边的端点为u,v,那么经过它的为cnt1[u]*cnt2[v],cnt1为正着最短路图上过u的边,cnt2为反着(注意到v结束也是一种,所以结尾后+1)。

因此,我们求两边拓扑排序即可,下面是AC代码(这里正反图用奇偶存储来区别):

#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;const int N=1502;
const int M=100010;
bool vis[N];
int dis[N],in1[N],in2[N],cnt1[N],cnt2[N];
int n,m,u,v,w,head[N],is[M],cnt=1;
int ans[M];
struct node{int dian,next,zhi;
}edge[M];
void add(int u,int v,int w){//i&1为反图 edge[++cnt].dian=v;edge[cnt].zhi=w;edge[cnt].next=head[u];head[u]=cnt;edge[++cnt].dian=u;edge[cnt].zhi=w;edge[cnt].next=head[v];head[v]=cnt; 
}
void spfa(int s){memset(vis,0,sizeof(vis));memset(dis,0x7f7f7f7f,sizeof(dis));queue<int> q;vis[s]=1;q.push(s);dis[s]=0;while(!q.empty()){int ck=q.front();q.pop();vis[ck]=0;for(int i=head[ck];i!=-1;i=edge[i].next){if(i&1) continue;if(dis[edge[i].dian]>dis[ck]+edge[i].zhi){dis[edge[i].dian]=dis[ck]+edge[i].zhi;if(!vis[edge[i].dian]){vis[edge[i].dian]=1;q.push(edge[i].dian);}}}}
}
void new1(){memset(is,0,sizeof(is));memset(in1,0,sizeof(in1));memset(in2,0,sizeof(in2));for(int u=1;u<=n;u++){for(int i=head[u];i!=-1;i=edge[i].next){if(i&1) continue;if(dis[u]+edge[i].zhi==dis[edge[i].dian]){is[i]=1;is[i^1]=1;in1[edge[i].dian]++;in2[u]++;}}}
}
void topo(int s){memset(cnt1,0,sizeof(cnt1));memset(cnt2,0,sizeof(cnt2));queue<int> q;q.push(s);cnt1[s]=1;while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=edge[i].next){if(!is[i]||(i&1)) continue;int v=edge[i].dian;in1[v]--;cnt1[v]=(cnt1[v]+cnt1[u])%mod;if(!in1[v]) q.push(v);}}for(int i=1;i<=n;i++){if(!in2[i]){cnt2[i]=1;q.push(i);}}while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=edge[i].next){if(!is[i]||!(i&1)) continue;int v=edge[i].dian;in2[v]--;cnt2[v]=(cnt2[v]+cnt2[u])%mod;if(!in2[v]){q.push(v);cnt2[v]++;//自己 }}}
}
void cal(){for(int u=1;u<=n;u++){for(int i=head[u];i!=-1;i=edge[i].next){if((i&1)||!is[i]) continue;int v=edge[i].dian;ans[i>>1]=(ans[i>>1]+cnt1[u]*cnt2[v]%mod)%mod;}}
}
void solve(){for(int i=1;i<=n;i++){spfa(i);new1();topo(i);cal();}for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
int main(){cin>>n>>m;memset(head,-1,sizeof(head));for(int i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);add(u,v,w);}solve();return 0;
}

2.最短路+DP

首先跑个最短路,然后我们令f[i][j]表示走到i不超过d+j的条数。

易得状态转移方程:f[x][k]=\sum(f[y][dis[x]+k-dis[y]-len2[x][y])%p;

至于顺序我们直接记忆化搜素即可。

这里有个比较麻烦的细节:

如何判断0环?

1.不是一有0环就-1,当你的0环不在最短路+k涉及的路径上它起不了作用,那么这如何判?

注意到此时dis[x]+k-dis[y]-len2[x][y]为负值,我们判一下这种情况即可。

2.当一个二维位置即同一个点,同一个小于距离出现时,说明0环,判-1.

因此我们还要用v[n][55]来记录有没有访问过(注意dfs完后归0操作)

3.注意到0环涉及起点1的情况,这也是为什么起点设在n+1源点而不是1的原因。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100000;
int t,n,m,k,p,a,b,c,flag;
vector<int> edge[N+1],len[N+1];
vector<int> edge1[N+1],len2[N+1];
int dis[N+1],vis[N+1];
int f[N+1][55];
bool v[N+1][55];
struct ty{int x,dis;bool operator< (const ty &a) const{return dis>a.dis;}
};
priority_queue<ty> q;
void dij(int s){memset(dis,0x7f,sizeof(dis));memset(vis,0,sizeof(vis));dis[s]=0;q.push({s,0});while(!q.empty()){ty tmp=q.top();q.pop();if(vis[tmp.x]) continue;vis[tmp.x]=1;for(int i=0;i<edge[tmp.x].size();i++){int y=edge[tmp.x][i];if(dis[y]>dis[tmp.x]+len[tmp.x][i]){dis[y]=dis[tmp.x]+len[tmp.x][i];q.push({y,dis[y]});}}}
}
int dfs(int x,int k){if(f[x][k]!=-1) return f[x][k];f[x][k]=0;v[x][k]=1;for(int i=0;i<edge1[x].size();i++){int y=edge1[x][i];int t=dis[x]+k-dis[y]-len2[x][i];if(t<0) continue;if(v[y][t]) flag=1;if(flag) return 0;    f[x][k]=(f[x][k]+dfs(y,t))%p;}v[x][k]=0;return f[x][k];
}
int main(){cin>>t;while(t--){scanf("%d%d%d%d",&n,&m,&k,&p);for(int i=1;i<=n+1;i++){edge[i].clear();len[i].clear();edge1[i].clear();len2[i].clear();}for(int i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);edge[a].push_back(b);len[a].push_back(c);edge1[b].push_back(a);len2[b].push_back(c);}edge[n+1].push_back(1);len[n+1].push_back(0);edge1[1].push_back(n+1);len2[1].push_back(0);dij(n+1);memset(f,-1,sizeof(f));memset(v,0,sizeof(v));f[n+1][0]=1;int ans=0;flag=0;for(int i=0;i<=k;i++){ans=(ans+dfs(n,i))%p;}if(flag) printf("-1\n");else printf("%d\n",ans);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/575552.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python+Django+Yolov5路面墙体桥梁裂缝特征检测识别html网页前后端

程序示例精选 PythonDjangoYolov5路面墙体桥梁裂缝特征检测识别html网页前后端 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjangoYolov5路面墙体桥梁裂缝特征检测识别html网页前…

UE小:基于UE5的两种Billboard material(始终朝向相机材质)

本文档展示了两种不同的效果&#xff0c;分别是物体完全朝向相机和物体仅Z轴朝向相机。通过下面的演示和相关代码&#xff0c;您可以更加直观地理解这两种效果的差异和应用场景。 1. 完全朝向相机效果 此效果下&#xff0c;物体将完全面向相机&#xff0c;不论相机在哪个角度…

浏览器工作原理与实践--块级作用域:var缺陷以及为什么要引入let和const

在前面《07 | 变量提升&#xff1a;JavaScript代码是按顺序执行的吗&#xff1f;》这篇文章中&#xff0c;我们已经讲解了JavaScript中变量提升的相关内容&#xff0c;正是由于JavaScript存在变量提升这种特性&#xff0c;从而导致了很多与直觉不符的代码&#xff0c;这也是Jav…

<QT基础(5)>事件监听

事件监听 事件监听&#xff08;Event Handling&#xff09;是在程序中监视和响应发生的事件的一种机制。在Qt中&#xff0c;事件监听是一种常见的用于处理用户输入、系统事件以及其他类型事件的方法。通过事件监听&#xff0c;您可以在发生特定事件时捕获事件并执行相应的操作…

状态模式实战运用

目录 前言 UML plantuml 类图 实战代码 Form State Client 前言 通常一个完整的业务流程中&#xff0c;会经历多个阶段&#xff0c;每个阶段即一个业务状态&#xff0c;不同状态下对应这不同的业务处理逻辑。 无脑堆砌 if else 做判断然后选择对应的业务处理其实也能…

Hive-技术补充-ANTLR的真实语法世界

一、上下文 上一篇博客<Hive-技术补充-ANTLR语法编写>&#xff0c;我们了解了如何使用ANTLR语法来表达词法结构和语法结构&#xff0c;下面我们循循渐进的处理身边用过的一些文件或语言&#xff1a; CSV、JSON、DOT、Cymbol、R 二、解析CSV文件 有这样一份csv文件 …

Hadoop面试重点

文章目录 1. Hadoop 常用端口号2.Hadoop特点3.Hadoop1.x、2.x、3.x区别 1. Hadoop 常用端口号 hadoop2.xhadoop3.x访问HDFS 端口500709870访问 MR 执行情况端口80888088历史服务器1988819888客户端访问集群端口90008020 2.Hadoop特点 高可靠&#xff1a;Hadoop底层维护多个数…

移动端开发思考:Uniapp的上位替代选择

文章目录 前言跨平台开发技术需求技术选型uniappFlutterMAUIAvalonia安卓原生 Flutter开发尝试Avalonia开发测试测试项目新建项目代码MainViewMainViewModel 发布/存档 MAUI实战&#xff0c;简单略过打包和Avalonia差不多 总结 前言 作为C# .NET程序员&#xff0c;我有一些移动…

【浅尝C++】使用模板实现泛型编程第一弹=>函数模板/类模板/模板匹配原则/函数模板原理

&#x1f3e0;专栏介绍&#xff1a;浅尝C专栏是用于记录C语法基础、STL及内存剖析等。 &#x1f6a9;一些备注&#xff1a;之前的文章有点杂乱&#xff0c;这里将前面的知识点重新组织了&#xff0c;避免了过多冗余的废话。 &#x1f3af;每日努力一点点&#xff0c;技术变化看…

MoonBit MeetUp回顾——张正、宗喆:编程语言在云原生与区块链领域的技术探索

宗喆和张正分别给我们带了 KCL 相关的最新进展&#xff0c;由蚂蚁集团开发的 Rust 编写的开源 DSL&#xff0c;目标是优化云原生策略配置和用户体验。它通过引入动态配置管理、配置校验和基础设施抽象等核心概念&#xff0c;解决开发者认知负担、配置膨胀和标准化工具缺乏的问题…

京东云0基础搭建帕鲁服务器_4核16G和8核32G幻兽帕鲁专用服务器

使用京东云服务器搭建幻兽帕鲁Palworld游戏联机服务器教程&#xff0c;非常简单&#xff0c;京东云推出幻兽帕鲁镜像系统&#xff0c;镜像直接选择幻兽帕鲁镜像即可一键自动部署&#xff0c;不需要手动操作&#xff0c;真正的新手0基础部署幻兽帕鲁&#xff0c;阿腾云atengyun.…

go的通信Channel

go的通道channel是用于协程之间数据通信的一种方式 一、channel的结构 go源码&#xff1a;GitHub - golang/go: The Go programming language src/runtime/chan.go type hchan struct {qcount uint // total data in the queue 队列中当前元素计数&#xff0c;…