zoukankan      html  css  js  c++  java
  • 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
    ",segTree[1].sum); 
    	} 
    }
  • 相关阅读:
    事务传播机制,搞懂。
    洛谷 P1553 数字反转(升级版) 题解
    洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here 题解
    洛谷 P1055 ISBN号码 题解
    洛谷 P2141 珠心算测验 题解
    洛谷 P1047 校门外的树 题解
    洛谷 P1980 计数问题 题解
    洛谷 P1008 三连击 题解
    HDU 1013 题解
    HDU 1012 题解
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14726600.html
Copyright © 2011-2022 走看看