森森从1号城市出发,先用现金支付路费,到某个城市后将所剩全部现金换成旅游金,用旅游金支付剩下的路费,直至到达n号城市。也就是说,我们要判断每一个转折城市k,求【1号城市到k号城市所需现金】+【k号城市到n号城市所需旅游金/k号城市的汇率】,值最小的那个,就是我们所求答案。
其实也是求两个单源最短路问题,一个是点1到各点所需的最少现金,另一个则是点n到各点所需的最少旅游金(前面分析的是说各点到点n的最少旅游金,但用n次dijkstra时间复杂度太高,而且我们只需要各点到点n的距离,各点到其他点的距离是没有用的)。第一个最短路我们以现金为距离建立正向图即可,第二个最短路我们以旅游金为距离建立反向图,注意需要两个head数组。由于点较多,我们可以用小根堆优化的dijkstra算法。
处理完两个距离数组后,我们就可以求解了。由于每次查询我们只需输出最少的现金数(最优解),因此我们仍可以用小根堆优化,先求出更新汇率前的所有解,加入堆。每次更新的时候,求出新解,入堆,小根堆的堆顶就是我们要的答案(但要及时弹出过时的答案,因为每次汇率更新都是建立在之前汇率调整的基础上的)。
#include <iostream>
#include <cstring>
#include <queue>
#define N 100010
#define M 400010
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
typedef long long LL;
struct node{//优先队列结点:{距离,中间点}
LL dist;
int id;
bool operator < (const node& a) const{//小根堆
return dist>a.dist;
}
};
int n,m,q,a[N],vis[N];
int h[2][N],ver[M],nxt[M],w[M],tot;//h[0][k]是现金正向图,h[1][k]是旅游金反向图
LL d1[N],d[N];//d1是1到各点的现金距离,d是n到各点的旅游金距离
void add(int u,int v,int c,int k){
ver[++tot]=v; w[tot]=c;
nxt[tot]=h[k][u]; h[k][u]=tot;
}
void dijk(int s,int k){
memset(d,0x3f,sizeof d);
memset(vis,0,sizeof vis);
priority_queue<node> pq;//堆优化
node t,r;
t.dist=d[s]=0;
t.id=s;
pq.push(t);
while(pq.size()){//dijkstra
t=pq.top(); pq.pop();
int u=t.id;
if(vis[u]) continue;
vis[u]=1;
for(int i=h[k][u];i;i=nxt[i]){
int v=ver[i];
if(!vis[v]&&d[v]>d[u]+w[i]){
r.dist=d[v]=d[u]+w[i];
r.id=v;
pq.push(r);
}
}
}
if(!k) memcpy(d1,d,sizeof d);
}
int main(){
scanf("%d%d%d",&n,&m,&q);
while(m--){
int u,v,c,d;
scanf("%d%d%d%d",&u,&v,&c,&d);
add(u,v,c,0),add(v,u,d,1);//现金正向图,旅游金反向图
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
dijk(1,0);//求1到各点的现金距离
dijk(n,1);//求n到各点的旅游金距离
priority_queue<node> pq;//堆优化
node t;
for(int i=1;i<=n;i++){//未调整时所有方案所需现金
if(d1[i]!=inf&&d[i]!=inf){
t.dist=d1[i]+(d[i]+a[i]-1)/a[i];//1用现金到i,从i用旅游金到n(等价于反向图n用旅游金到i)
t.id=i;
pq.push(t);
}
}
while(q--){
int x;
cin>>x>>a[x];
if(d1[x]!=inf&&d[x]!=inf){//更新距离
t.dist=d1[x]+(d[x]+a[x]-1)/a[x];
t.id=x;
pq.push(t);
}
while(pq.size()){
t=pq.top();
x=t.id;
if(t.dist!=d1[x]+(d[x]+a[x]-1)/a[x]) pq.pop();//及时排除过时信息
else break;
}
printf("%lld\n",t.dist);//堆顶值就是答案,注意,不需要弹出
}
return 0;
}