GPLT L3-028.森森旅游
建两个图,正图和反图。
正图里的边权是现金,反图里的边权是旅游金。
然后分别以1为起点在正图上跑最短路,以n为起点在反图上跑最短路。
这样计算出每个点的答案,取最小,不带修改的情况就做完了。
带修改的情况放线段树上维护一下就好了。
注意巨大坑点:不保证图连通。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
typedef long long ll;
const ll inf=1e18;
vector<pair<int,int> > g1[maxn];//原图
vector<pair<int,int> > g2[maxn];//反图
int n,m,q;
int a[maxn];//汇率
ll d1[maxn];//1到每个点的现金最短路
ll d2[maxn];//每个点到n的旅游金最短路
struct qnode {
//dijkstra辅助结构体
int v;
ll w;
qnode (int vv,ll ww) {
v=vv;
w=ww;
}
bool operator < (const qnode &r) const {
return w>r.w;//结构体重载运算符
//因为STL的优先队列默认是大顶堆,所以这里反着写运算符
}
};
int vis[maxn];//dij标记是否确定最短路
void dij1 (int s) {
//以1为起点,在正图上跑第一遍最短路
for (int i=1;i<=n;i++) d1[i]=inf,vis[i]=0;
d1[s]=0;
priority_queue<qnode> q;//堆优化
q.push(qnode(s,d1[s]));
while (q.size()) {
qnode tt=q.top();
q.pop();
int u=tt.v;
if (vis[u]) continue;
vis[u]=1;
for (pair<int,int> it:g1[u]) {
int v=it.first;
if (vis[v]) continue;
if (d1[u]+it.second<d1[v]) {
d1[v]=d1[u]+it.second;
q.push(qnode(v,d1[v]));
}
}
}
}
void dij2 (int s) {
//以n为起点,在反图上跑第二遍最短路
for (int i=1;i<=n;i++) d2[i]=inf,vis[i]=0;
d2[s]=0;
priority_queue<qnode> q;//堆优化
q.push(qnode(s,d2[s]));
while (q.size()) {
qnode tt=q.top();
q.pop();
int u=tt.v;
if (vis[u]) continue;
vis[u]=1;
for (pair<int,int> it:g2[u]) {
int v=it.first;
if (vis[v]) continue;
if (d2[u]+it.second<d2[v]) {
d2[v]=d2[u]+it.second;
q.push(qnode(v,d2[v]));
}
}
}
}
ll ans[maxn];//每个点的答案
struct node {
int l,r;
ll sum;//区间最小值
}segTree[maxn<<2];
void build (int i,int l,int r) {
segTree[i].l=l;
segTree[i].r=r;
if (l==r) {
segTree[i].sum=ans[l];
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
segTree[i].sum=min(segTree[i<<1].sum,segTree[i<<1|1].sum);
}
void up (int i,int p) {
if (segTree[i].l==p&&segTree[i].r==p) {
segTree[i].sum=d1[p]+d2[p]/a[p]+(d2[p]%a[p]>0?1:0);
return;
}
int mid=(segTree[i].l+segTree[i].r)>>1;
if (p<=mid) up(i<<1,p);
if (p>mid) up(i<<1|1,p);
segTree[i].sum=min(segTree[i<<1].sum,segTree[i<<1|1].sum);
}
int main () {
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;i++) {
int A,B,C,D;
scanf("%d%d%d%d",&A,&B,&C,&D);
g1[A].push_back(make_pair(B,C));
g2[B].push_back(make_pair(A,D));
}
for (int i=1;i<=n;i++) scanf("%d",a+i);
dij1(1);
dij2(n);
for (int i=1;i<=n;i++) {
if (d1[i]==inf||d2[i]==inf) {
ans[i]=1e18;
continue;
}
ans[i]=d1[i]+d2[i]/a[i]+(d2[i]%a[i]>0?1:0);
}
build(1,1,n);//线段树建树
while (q--) {
int x,y;
scanf("%d%d",&x,&y);
a[x]=y;
if (d1[x]!=inf&&d2[x]!=inf)
up(1,x);//单点更新
printf("%lld\n",segTree[1].sum);
}
}