PTA 7-9 森森旅游(30分)

好久没出去旅游啦!森森决定去 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→4→6 的线路旅行,并在 2 号城市兑换旅游金;

对于第二次汇率调整,森森可以沿着 1→2→3→4→6 的线路旅行,并在 3 号城市兑换旅游金;

对于第三次汇率调整,森森可以沿着 1→3→5→6 的线路旅行,并在 1 号城市兑换旅游金。

限制条件

限制条件数量
代码长度限制16 KB
Java (javac)限制条件数量
时间限制3000 ms
内存限制384 MB
Python (python3)限制条件数量
时间限制2000 ms
内存限制128 MB
其他编译器限制条件数量
时间限制1000 ms
内存限制128 MB

C++代码

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <set>

using namespace std;

typedef long long LL;
typedef pair <LL,int> PII;

const int N = 100010 , M = 200010 * 2;
const LL inf = 0x3f3f3f3f3f3f3f3fll;


struct e
{
    int to; //终点
    int w; //权重
    int next; //与这个边起点相同的上一条边的编号
};
struct e edge[M];

int h1[N], h2[N];
int tot;
int vis[N];
int n, m, q;
int rate[N];
LL dist1[N], dist2[N];

//添加有权边
void addedge(int head[], int a, int b, int c)
{
    edge[tot].to = b; //终点
    edge[tot].w = c; //权重
    edge[tot].next = head[a]; //与这个边起点相同的上一条边的编号
    head[a] = tot++;
}

//堆优化的dijkstra算法
void dijkstra(int head[], LL dist[], int start)
{
     memset(dist, 0x3f, sizeof dist1);
     dist[start] = 0;
     memset(vis, 0, sizeof vis);

     priority_queue < PII, vector<PII>, greater<PII> > heap;

     heap.push({0, start}); //把起点添加到优先队列heap

     while(heap.size())
     {
         int v = heap.top().second;
         heap.pop();

         if(vis[v]) //找到还没有访问的点
         {
             continue;
         }

         for(int i = head[v]; i != -1; i = edge[i].next)
         {
             int j = edge[i].to;
             if(dist[j] > dist[v] + edge[i].w)
             {
                 dist[j] = dist[v] + edge[i].w;
                 heap.push({dist[j], j});
             }
         }

         vis[v] = true;
     }
}

long long ceil(long long a, int b) //向上取整
{
    if(a % b == 0)
    {
        return a / b;
    }
    else
    {
        return a / b + 1;
    }
}

int main(){

    cin >> n >> m >> q;
    memset(h1, -1, sizeof h1);
    memset(h2, -1, sizeof h2);

    for(int i = 0; i < m; i++)
    {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        addedge(h1, a, b, c);
        addedge(h2, b, a, d);
    }

    for(int i = 1; i <= n; i++)
    {
        cin >> rate[i];
    }

    dijkstra(h1, dist1, 1);
    dijkstra(h2, dist2, n);

    multiset<LL> s; //能时刻保证序列中的数是有序的,而且序列中可以存在重复的数

    for(int i = 1;i <= n; i ++ ) //在每个点换旅游金的情况,存入s
    {
    //必须要可以连通
        if(dist1[i] != inf && dist2[i] != inf)
        {
        //此时对于后面需要的代金券兑换的现金需要向上取整
            s.insert(dist1[i] + ceil(dist2[i], rate[i]));
        }
    }

    while(q--)
    {
        int a, b; //第a个城市汇率改为b
        cin >> a >> b;
        if(dist1[a] != inf && dist2[a] != inf) //如果它是连通的
        {
            s.erase(s.find(dist1[a] + ceil(dist2[a], rate[a])));
            rate[a] = b;
            s.insert(dist1[a] + ceil(dist2[a], rate[a]));
        }

        cout << *s.begin() << endl;
    }

    return 0;
}

运行结果

请添加图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值