PTA 森森旅游

文章描述了一个关于Z省旅游的问题,其中涉及多个城市、旅行线路和费用支付方式,包括现金和旅游金。政府推出旅游金计划,允许在不同城市按汇率兑换。森森需要从1号城市到n号城市,求每次汇率调整后所需的最少现金。解决方案包括计算起点到每个点的现金消耗和反向旅行币消耗,并根据汇率调整更新路径成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好久没出去旅游啦!森森决定去 Z 省旅游一下。

Z 省有 n 座城市(从 1 到 n 编号)以及 m 条连接两座城市的有向旅行线路(例如自驾、长途汽车、火车、飞机、轮船等),每次经过一条旅行线路时都需要支付该线路的费用(但这个收费标准可能不止一种,例如车票跟机票一般不是一个价格)。

Z 省为了鼓励大家在省内多逛逛,推出了旅游金计划:在 i 号城市可以用 1 元现金兑换 ai​ 元旅游金(只要现金足够,可以无限次兑换)。城市间的交通即可以使用现金支付路费,也可以用旅游金支付。具体来说,当通过第 j 条旅行线路时,可以用 cj​ 元现金 dj​ 元旅游金支付路费。注意: 每次只能选择一种支付方式,不可同时使用现金和旅游金混合支付。但对于不同的线路,旅客可以自由选择不同的支付方式。

森森决定从 1 号城市出发,到 n 号城市去。他打算在出发前准备一些现金,并在途中的某个城市将剩余现金 全部 换成旅游金后继续旅游,直到到达 n 号城市为止。当然,他也可以选择在 1 号城市就兑换旅游金,或全部使用现金完成旅程。

Z 省政府会根据每个城市参与活动的情况调整汇率(即调整在某个城市 1 元现金能换多少旅游金)。现在你需要帮助森森计算一下,在每次调整之后最少需要携带多少现金才能完成他的旅程。

输入格式:

输入在第一行给出三个整数 n,m 与 q(1≤n≤105,1≤m≤2×105,1≤q≤105),依次表示城市的数量、旅行线路的数量以及汇率调整的次数。

接下来 m 行,每行给出四个整数 u,v,c 与 d(1≤u,v≤n,1≤c,d≤109),表示一条从 u 号城市通向 v 号城市的有向旅行线路。每次通过该线路需要支付 c 元现金或 d 元旅游金。数字间以空格分隔。输入保证从 1 号城市出发,一定可以通过若干条线路到达 n 号城市,但两城市间的旅行线路可能不止一条,对应不同的收费标准;也允许在城市内部游玩(即 u 和 v 相同)。

接下来的一行输入 n 个整数 a1​,a2​,⋯,an​(1≤ai​≤109),其中 ai​ 表示一开始在 i 号城市能用 1 元现金兑换 ai​ 个旅游金。数字间以空格分隔。

接下来 q 行描述汇率的调整。第 i 行输入两个整数 xi​ 与 ai′​(1≤xi​≤n,1≤ai′​≤109),表示第 i 次汇率调整后,xi​ 号城市能用 1 元现金兑换 ai′​ 个旅游金,而其它城市旅游金汇率不变。请注意:每次汇率调整都是在上一次汇率调整的基础上进行的。

输出格式:

对每一次汇率调整,在对应的一行中输出调整后森森至少需要准备多少现金,才能按他的计划从 1 号城市旅行到 n 号城市。

再次提醒:如果森森决定在途中的某个城市兑换旅游金,那么他必须将剩余现金全部、一次性兑换,剩下的旅途将完全使用旅游金支付。

输入样例:

6 11 3
1 2 3 5
1 3 8 4
2 4 4 6
3 1 8 6
1 3 10 8
2 3 2 8
3 4 5 3
3 5 10 7
3 3 2 3
4 6 10 12
5 6 10 6
3 4 5 2 5 100
1 2
2 1
1 17

输出样例:

8
8
1

思路:

先分别计算起点到每个点的现金消耗,每个点到终点的旅行币消耗(即存反图终点到每个点);

先遍历(1,2, .... , n)换钱需要的现金

再删除汇率更新前消耗的现金量,添加汇率更新后需要的现金量

输出目前最小的现金消耗;

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<ll,int>
#define me(a,b) memset(a,b,sizeof a)
ll n , m , T , idx , cnt;
const ll l = 400005; 
const ll r = 100005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
string s;
int er[r];
ll ne_1[l] , e_1[l] , w_1[l] , h_1[l]; 
ll ne_2[l] , e_2[l] , w_2[l] , h_2[l];
ll d_1[l] , d_2[l];
bool st[r];
void add_1(int a ,int b ,int c){
	ne_1[idx] = h_1[a];
	e_1[idx] = b;
	w_1[idx] = c;
	h_1[a] = idx ++;
}
void add_2(int a,int b,int c){
	ne_2[cnt] = h_2[a];
	e_2[cnt] = b;
	w_2[cnt] = c;
	h_2[a] = cnt ++;
}
void bfs_1(int a){
	me(st,0);
	for(int i = 1 ; i <= n ; i ++)d_1[i] = inf;
	priority_queue<pii,vector<pii>,greater<pii> > q;
	q.push({0,a}); d_1[a] = 0;
	while(!q.empty()){
		auto t = q.top().second;
		q.pop();
		if(st[t])continue;
		st[t] = 1;
		for(int i = h_1[t] ; i != -1 ; i = ne_1[i]){
			int x = e_1[i];
			if(d_1[x] <= d_1[t] + w_1[i])continue;
				d_1[x] = d_1[t] + w_1[i];
				q.push({d_1[x],x});
		}
	}
} 
void bfs_2(int a){
	me(st,0);
	for(int i = 1 ; i <= n ; i ++)d_2[i] = inf;
	priority_queue<pii,vector<pii>,greater<pii> > q;
	q.push({0,a});
	d_2[a] = 0;
	while(!q.empty()){
		int t = q.top().second;
		q.pop();
		if(st[t])continue;
		st[t] = 1;
		for(int i = h_2[t] ; ~ i ; i = ne_2[i]){
			int x = e_2[i];
			if(d_2[x] <= d_2[t] + w_2[i])continue;
				d_2[x] = d_2[t] + w_2[i];
				q.push({d_2[x],x});
		}
	}
}
int main(){
	int q;
	cin >> n >> m >> q;
	idx = cnt = 0;//两个链式前向星的下标归零 
	me(h_1,-1);
	me(h_2,-1);//初始化 
	while(m --){
		int a , b , c , d;
		cin >> a >> b >> c >> d;
		add_1( a , b , c);//第一个存使用现金的1 - > n 
		add_2( b , a , d);//第二个存使用旅行币n - > 1 
	}
	bfs_1(1);
	bfs_2(n);
	//跑出1->n现金消耗和(1,2,3,4...,n->n)的旅行币消耗
	//分别存入d_1 和 d_2 
	for(int i = 1 ; i <= n ; i ++)cin >> er[i];
	//读取各个汇率 
	map<ll,int>mp;
	for(int i = 1 ; i <= n ; i++){
		if(d_1[i] == inf || d_2[i] == inf)continue;
		mp[d_1[i] + (d_2[i] + (er[i] - 1))/er[i]]++;
	}
	//d_1[i] + (d_2[i] + (er[i] - 1))/er[i]等于 
	// 1->i的现金消耗 + i->n的旅行币消耗/汇率 
	while(q --){
		int a , b;
		cin >> a >> b;
		if(d_1[a] != inf && d_2[a] != inf){
			mp[d_1[a] + ((d_2[a] + (er[a] - 1))/er[a])]--;
			//删除原汇率时,在这个城市换钱所需要的现金 
			if(mp[d_1[a] + ((d_2[a] + (er[a] - 1))/er[a])] == 0){
				mp.erase(d_1[a] + ((d_2[a] + (er[a] - 1))/er[a]));
			}
			//如果该现金的情况变为0,就在map中删除这个 
			er[a] = b;
			//添加新汇率时目前的现金消耗 
			mp[d_1[a] + (d_2[a] + (er[a] - 1))/er[a]]++;
		}
		for(auto i : mp){
			cout << i.first << endl; 
			break;
		}
		//输出最小的现金消耗 
	} 
	
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值