L3-028 森森旅游 - 团体程序设计天梯赛-练习集 (pintia.cn)
解析:
因为可以选择在一个城市全部兑换,就可以把整个旅程分为兑换前和兑换后的旅程。
假设在编号 x 城市兑换,这样就转化成 1 到 x 用现金支付的最短路 x 到 n 用旅游金支付的最短路。
不过要注意因为是有向图,所以1 到 x 建立正向图,x 到 n 就得建立反向图。
之后就是关于单点修改和查询最小值了,我用的是小根堆+map。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
// int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
// typedef unsigned long long ULL;
typedef pair<int,int> PII;
// const double PI = acos(-1.0);
const int N=1e5+10;
int n,m,p;
struct node
{
int v,cw,dw;
};
int a[N];
vector<node> g[N],h[N];
int d1[N];
int d2[N];
bool vis[N];
priority_queue <PII,vector<PII>,greater<PII>> q;
void djkstra1()
{
for (int i=1;i<=n;i++) d1[i]=1e18,vis[i]=0;
d1[1]=0;
q.push({0,1});
while (q.size())
{
int tance=q.top().first;
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=1;
for (auto [v,cw,dw]:g[u])
{
if (d1[v]>tance+cw)
{
d1[v]=tance+cw;
q.push({d1[v],v});
}
}
}
}
void djkstra2()
{
for (int i=1;i<=n;i++) d2[i]=1e18,vis[i]=0;
d2[n]=0;
q.push({0,n});
while (q.size())
{
int tance=q.top().first;
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=1;
for (auto [v,cw,dw]:h[u])
{
if (d2[v]>tance+dw)
{
d2[v]=tance+dw;
q.push({d2[v],v});
}
}
}
}
priority_queue <int,vector<int>,greater<int>> s;
map <int,int> l;
void solve()
{
cin>>n>>m>>p;
for (int i=0;i<m;i++)
{
int u,v,cw,dw;
cin>>u>>v>>cw>>dw;
g[u].push_back({v,cw,dw});
h[v].push_back({u,cw,dw});
}
for (int i=1;i<=n;i++) cin>>a[i];
djkstra1();
djkstra2();
for (int i=1;i<=n;i++)
{
if (d1[i]>1e18/2||d2[i]>1e18/2) continue;
int k=d1[i]+(d2[i]+a[i]-1)/a[i];
s.push(k);
}
while (p--)
{
int x,y;
cin>>x>>y;
if (d1[x]>1e18/2||d2[x]>1e18/2)
{
cout<<s.top()<<endl;
continue;
}
int x1=d1[x]+(d2[x]+a[x]-1)/a[x];
a[x]=y;
int x2=d1[x]+(d2[x]+a[x]-1)/a[x];
l[x1]++;
s.push(x2);
while (l[s.top()]>0) l[s.top()]--,s.pop();
cout<<s.top()<<endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
//cin >> T;
while (T--) solve();
return 0;
}