0
点赞
收藏
分享

微信扫一扫

森林旅游 双向dijkstra +multiset的使用

small_Sun 2022-04-16 阅读 85
c++
//思路是从某个点分开,前面用现金,后面用游览币
//即求出 从头到尾用金币 从尾到头用游览币
// 循环n次  找出从某个点兑换时产生的最小值
// 要点,如何短时间内找出很多数中的最小值  
//数据很大,long long 才可以
#include<bits/stdc++.h>
using namespace std;
typedef pair<long long,long long> PII; 
const long long mmm=0x3f3f3f3f3f3f3f3f;
int n,m,q;
int idx,e[400005],ne[400005],h1[100005],w[400005],h2[100005];
void add1(int a,int b,int c)
{//正向建边 
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h1[a];
	h1[a]=idx++;
}
void add2(int a,int b,int c)
{//反向建边 
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h2[a];
	h2[a]=idx++;
}
int a[100005];//汇率 
long long dist1[100005];//正向走到i点花费的现金 
long long dist2[100005];//反向走到i点花费的游览币 
bool   st[100005];//是否走过----dijkstra算法模板 
void dijkstra1()
{//正向模板 
	memset(dist1,0x3f,sizeof dist1);
	priority_queue<PII,vector<PII>,greater<PII>> heap;
	heap.push({0,1});
	dist1[1]=0;
	while(heap.size())
	{
		auto t=heap.top();heap.pop();
		int ver=t.second;
		if(st[ver])continue;
		st[ver]=1;
		for(int i=h1[ver];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(dist1[j]>dist1[ver]+w[i])
			{
				dist1[j]=dist1[ver]+w[i];
				if(!st[j])
				heap.push({dist1[j],j});
			}
		}
	}	
}
void dijkstra2()
{//反向模板 
	memset(st,0,sizeof st);
	memset(dist2,0x3f,sizeof dist2);
	priority_queue<PII,vector<PII>,greater<PII>>heap;
	dist2[n]=0;
	heap.push({0,n});
	while(heap.size())
	{
		auto t=heap.top();heap.pop();
		int ver=t.second;
		if(st[ver])continue;
		st[ver]=1;
		for(int i=h2[ver];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(dist2[j]>dist2[ver]+w[i])
			{
				dist2[j]=dist2[ver]+w[i];
				heap.push({dist2[j],j});
			}
		}
	}
	
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	memset(h1,-1,sizeof h1);
	memset(h2,-1,sizeof h2);
	for(int i=1;i<=m;i++)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		add1(a,b,c);//正向建边 
		add2(b,a,d);//反向建边 
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	
	
	dijkstra1();
	dijkstra2();
	multiset<long long> S;要点  -----可以修改的查找数组最小值的方法;--不同于单调队列的不可修改性 
	for(int i=1;i<=n;i++)
	{
		if(dist1[i]!=mmm&&dist2[i]!=mmm)
		{
			S.insert(dist1[i]+(dist2[i]+a[i]-1)/a[i]);
			//将第i点兑换所需的金币存入 
		}
	}
	while(q--)
	{
		int w,b;
		scanf("%d",&w);
		scanf("%d",&b);
		if(dist1[w]!=mmm&&dist2[w]!=mmm)
		{
			S.erase(S.find(dist1[w]+(dist2[w]+a[w]-1)/a[w]));//删除此点 
			a[w]=b;
			S.insert(dist1[w]+(dist2[w]+a[w]-1)/a[w]);//加入此点 
		}
		printf("%lld\n",*S.begin());//可以直接找出最小值----log(n)复杂度 
	}
}
举报

相关推荐

0 条评论