图论之路径条数专题

news2024/9/22 5:25:28

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

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.coloradmin.cn/o/1552855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【笔记】RDD算子操作(Spark基础知识)

持续更新中&#xff01;&#xff01;&#xff01; 目录 一、RDD的创建 1.从本地创建 &#xff08;1&#xff09;本地文件 &#xff08;2&#xff09;hdfs文件&#xff08;先提前创建目录并上传文件&#xff09; 2.从集合创建&#xff08;通过并行集合&#xff08;列表&am…

C#手术麻醉信息系统全套商业源码,自主版权,支持二次开发 医院手麻系统源码

手术麻醉信息系统是HIS产品的中的一个组成部分&#xff0c;主要应用于医院的麻醉科&#xff0c;属于电子病历类产品。医院麻醉监护的功能覆盖整个手术与麻醉的全过程&#xff0c;包括手术申请与排班、审批、安排、术前、术中和术后的信息管理提供支持。 手术麻醉信息系统可与EM…

MTK8781安卓核心板_MT8781(Helio G99)核心板性能参数

MT8781安卓核心板搭载了八核CPU&#xff0c;其中包括两个主频高达2.2GHz的高性能Arm Cortex-A76处理器。这一处理器采用了台积电6纳米级芯片生产工艺&#xff0c;以及先进的3D图形功能的高性能Arm Mali G57级GPU。通过超快LPDDR4X内存和UFS 2.2存储供电&#xff0c;不仅提高了游…

springdata框架对es集成

什么是spring data框架 Spring Data是一个用于简化数据库、非关系型数据库、索引库访问&#xff0c;并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷&#xff0c;并支持 map-reduce框架和云计算数据服务。Spring Data可以极大的简化JPA(Elasticsearch…)的…

【TB作品】MSP430G2553,超声波倒车雷达PCB,单片机,超声波SR04,键盘,oled,

题目 硬件&#xff1a;MSP430G2553、 SR04超声波传感器 、3*4键盘、 无源蜂鸣器、oled显示屏 软件 1 、实时显示测量得到的距离 2、按键设置一个报警门限数值&#xff0c;直接输入数值后确认 3、低于报警门限数值就开始报警&#xff0c;而且距离越近蜂鸣器的鸣叫频率越高 程序…

电脑突然死机怎么办?

死机是电脑常见的故障问题&#xff0c;尤其是对于老式电脑来说&#xff0c;一言不合电脑画面就静止了&#xff0c;最后只能强制关机重启。那么你一定想知道是什么原因造成的吧&#xff0c;一般散热不良最容易让电脑死机&#xff0c;还有系统故障&#xff0c;比如不小心误删了系…

MySQL count(*/column)查询优化

count()是SQL中一个常用的聚合函数&#xff0c;其被用来统计记录的总数&#xff0c;下面通过几个示例来说明此类查询的注意事项及应用技巧。 文章目录 一、count()的含义二、count()的应用技巧2.1 同时统计多列2.2 利用执行计划 一、count()的含义 count()用于统计符合条件的记…

Yarn简介及Windows安装与使用指南

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

STM32使用USART发送数据包指令点亮板载LED灯

电路连接&#xff1a; 连接显示屏模块&#xff0c;显示屏的SCL在B10&#xff0c;SDA在B11。 程序目的&#xff1a; 发送LED_ON指令打开板载LED灯&#xff0c;发送LED_OFF关闭板载LED灯&#xff0c;与上一个博客不同&#xff0c;这个实际上是实现串口收发文本数据包。 …

flink on yarn-per job源码解析、flink on k8s介绍

Flink 架构概览–JobManager JobManager的功能主要有: 将 JobGraph 转换成 Execution Graph,最终将 Execution Graph 拿来运行Scheduler 组件负责 Task 的调度Checkpoint Coordinator 组件负责协调整个任务的 Checkpoint,包括 Checkpoint 的开始和完成通过 Actor System 与 …

【MySQL】6.MySQL主从复制和读写分离

主从复制 主从复制与读写分离 通常数据库的读/写都在同一个数据库服务器中进行&#xff1b; 但这样在安全性、高可用性和高并发等各个方面无法满足生产环境的实际需求&#xff1b; 因此&#xff0c;通过主从复制的方式同步数据&#xff0c;再通过读写分离提升数据库的并发负载…

Day54:WEB攻防-XSS跨站Cookie盗取表单劫持网络钓鱼溯源分析项目平台框架

目录 XSS跨站-攻击利用-凭据盗取 XSS跨站-攻击利用-数据提交 XSS跨站-攻击利用-flash钓鱼 XSS跨站-攻击利用-溯源综合 知识点&#xff1a; 1、XSS跨站-攻击利用-凭据盗取 2、XSS跨站-攻击利用-数据提交 3、XSS跨站-攻击利用-网络钓鱼 4、XSS跨站-攻击利用-溯源综合 漏洞原理…

深度学习理解及学习推荐(持续更新)

主推YouTuBe和Bilibili 深度学习博主推荐&#xff1a; Umar Jamil - YouTubehttps://www.youtube.com/umarjamilai StatQuest with Josh Starmer - YouTubehttps://www.youtube.com/statquest RNN Illustrated Guide to Recurrent Neural Networks: Understanding the Int…

知乎:多云架构下大模型训练,如何保障存储稳定性?

知乎&#xff0c;中文互联网领域领先的问答社区和原创内容平台&#xff0c;2011 年 1 月正式上线&#xff0c;月活跃用户超过 1 亿。平台的搜索和推荐服务得益于先进的 AI 算法&#xff0c;数百名算法工程师基于数据平台和机器学习平台进行海量数据处理和算法训练任务。 为了提…

生成式 AI 学习资源大汇总

这里汇聚了该领域的海量学习资源&#xff0c;从研究更新到面试技巧&#xff0c;从课程材料到免费课程&#xff0c;还有实用代码&#xff0c;一应俱全&#xff0c;是你工作流程中的得力助手&#xff01; 前沿研究&#xff1a;每月精心筛选的最佳生成式 AI 论文列表&#xff0c;让…

Flink集群主节点JobManager启动分析

1.概述 JobManager 是 Flink 集群的主节点&#xff0c;它包含三大重要的组件&#xff1a; ResourceManager Flink集群的资源管理器&#xff0c;负责slot的管理和申请工作。 Dispatcher 负责接收客户端提交的 JobGraph&#xff0c;随后启动一个Jobmanager&#xff0c;类似 Yarn…

C#全新一代医院手术麻醉系统围术期全流程源码

目录 一、麻醉学科的起源 二、麻醉前访视与评估记录单 患者基本信息 临床诊断 患者重要器官功能及疾病情况 病人体格情况分级 手术麻醉风险评估 拟施麻醉方法及辅助措施 其他需要说明的情况 访视麻醉医师签名 访视时间 与麻醉相关的检查结果 三、手术麻醉信息系统…

蓝桥杯单片机快速开发笔记——PCF8591的DAC模拟电压输出

一、原理分析 PCF8591电压信号探测器&#xff1a;http://t.csdnimg.cn/R38tC IIC原理&#xff1a;http://t.csdnimg.cn/v4dSv IIC指令&#xff1a;http://t.csdnimg.cn/RY6yi HC573/HC138&#xff1a;http://t.csdnimg.cn/W0a0U 数码管&#xff1a;http://t.csdnimg.cn/kfm9Y 独…

jmeter总结之:Regular Expression Extractor元件

Regular Expression Extractor是一个后处理器元件&#xff0c;使用正则从服务器的响应中提取数据&#xff0c;并将这些数据保存到JMeter变量中&#xff0c;以便在后续的请求或断言中使用。在处理动态数据或验证响应中的特定信息时很有用。 添加Regular Expression Extractor元…

实时数仓之实时数仓架构(Hudi)

目前比较流行的实时数仓架构有两类&#xff0c;其中一类是以FlinkDoris为核心的实时数仓架构方案&#xff1b;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍&#xff0c;这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…