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); 
	} 
} 
posted @ 2021-05-02 21:51  zlc0405  阅读(105)  评论(0编辑  收藏  举报