L3-028 森森旅游 (30 分)

还没题单把..

怪无语的这题。我开始把更新最值写反了。没想到还给过了样例,交上去就1分。然后就gg了。

可能很多人没整体思路?

先暴力吧。有26分?

枚举中间的换钱点,对于前面纸币的,跑一个最短路。对于后面的旅游币,跑一个最短路。

这时候有一个最值答案。

但是维护的话单纯看是否有一个比当前值更小是不对的。因为如果一开始total存的就是最小值,假如说是第x个,然后现在把x的汇率改小了,那么你就要花更多的钱。此时答案是未知的。

于是这里可以暴力阿..我真是麻了以后就该cccc先给暴力过去的。

 

那如果要优化呢?我们注意到可以把答案的值都扔到multiset里去,然后每次更新把当前点原来的答案在multiset里去掉,然后更新后给扔回去。此时最小值就是整体最优答案。

 

整体复杂度O(2nlogm+q*logn^2)

 

如果这题改成给一个区间l,r,你只能在这个区间内的点兑换旅游币呢?(Hint:线段树维护)

 

注意1e18的特判,不然答案还是可能被更新掉。因为1e18代表不可到达

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<cstdio>
#include<set>
#include<map>
#include<queue>
#define lowbit(x) x&(-x)
#define debug(a) cout<<"debug:"<<#a<<"\n"
using namespace std;
typedef long long LL;
typedef pair<LL,LL>P;///first距离,second编号
const LL mod=1e9+7;
const LL maxn=1e5+1000;
struct Edge{
    LL to,val;
};
vector<Edge>g1[maxn];
vector<Edge>g4[maxn];
multiset<LL>s1;
LL dis1c[maxn],disnd[maxn];
LL a[maxn];
LL n,m,q;
bool vis[maxn];
void dijkstra(LL st, vector<Edge>g[], LL dis[]){
     memset(vis,0,sizeof(vis));
     for(LL i=0;i<n+10;i++) dis[i]=1e18;
     priority_queue< P,vector<P>, greater<P> >que;
     dis[st]=0;
     que.push({dis[st],st});
     while(!que.empty()){
        P now=que.top();que.pop();
        LL u=now.second;
        if(vis[u]) continue;
        vis[u]=1;
        for(LL i=0;i<g[u].size();i++){
            LL v=g[u][i].to;
            LL cost=g[u][i].val;
            if(dis[v]>dis[u]+cost){
                dis[v]=dis[u]+cost;
                que.push({dis[v],v});
            }
        }
     }
}
int main(void){
   cin.tie(0);std::ios::sync_with_stdio(false);
   cin>>n>>m>>q;
   for(LL i=1;i<=m;i++){
       LL u,v,c,d;cin>>u>>v>>c>>d;
       g1[u].push_back({v,c});
       g4[v].push_back({u,d});
   }
   for(LL i=1;i<=n;i++) cin>>a[i];

   dijkstra(1,g1,dis1c);
   dijkstra(n,g4,disnd);

   for(LL i=1;i<=n;i++){///枚举每个点作为中转
       if(dis1c[i]==1e18||disnd[i]==1e18){
            continue;///不更新		
       }
       LL need=dis1c[i]+(disnd[i]+a[i]-1)/a[i];
       s1.insert(need);
   }
//优化:提前把答案都扔到multiset里,然后logn更新最值
   while(q--){
      LL id,num;cin>>id>>num;
      if(dis1c[id]==1e18||disnd[id]==1e18){
            cout<<(*s1.begin())<<"\n";
            continue;///不更新		
      }
      LL temp=dis1c[id]+(disnd[id]+a[id]-1)/a[id];
      s1.erase(s1.find(temp));

      a[id]=num;

      temp=dis1c[id]+(disnd[id]+a[id]-1)/a[id];
      s1.insert(temp);
      cout<<(*s1.begin())<<"\n";
//      LL mx=2e18;
//      for(LL i=1;i<=n;i++){
//         //由于不知道本次更新的点是否是原来提供最优解的点,暴力更新24分.
//         LL need=dis1c[i]+(disnd[i]+a[i]-1)/a[i];
//         mx=min(mx,need);
//      }
//      cout<<mx<<"\n";

   }
   return 0;
}

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值