“新华三杯”第十届成都信息工程大学ACM程序设计竞赛(同步赛)L. 怎么走啊(最短路+二分 分段函数)

本文讨论了如何使用Dijkstra算法解决最短路径问题,通过二分法优化处理W值变化带来的影响,指出在关注1到n最短路时,复杂度可视为O(nsqrt(n)lognlogn),并在牛客网背景下给出了代码实现和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

登录—专业IT笔试面试备考平台_牛客网

思路来源

衡阳师范学院ac代码、pj学弟

题解

大致可以证明,在w从1e5减小到1的过程中,

之前某条反向边没有用到,现在需要用到反向边,也就是正向边用到的变少了

这样的变化有sqrt个,二分每个变化时的临界点,复杂度似乎是O(nsqrtnlognlogn)的

但是由于只关注1到n的最短路,临界点&二分的量级很难卡满,只能说O(能过)了

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define endl "\n"
#define IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int ll
const int N=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
typedef pair<int,int> pii;
int n,m,d[N],st[N],ans[N],pre[N];
vector<array<int,3>> g[N];
int dijkstra(int W){
    for(int i=1;i<=n;i++) st[i]=0,d[i]=1e18;
    priority_queue<pii,vector<pii>,greater<pii>> q;
    q.push({0,1}); d[1]=0;
    while(!q.empty()){
        auto [dt,u]=q.top(); q.pop();
        if(st[u]) continue;
        st[u]=1;
        for(auto [j,w,f]:g[u]){
            int dis=dt;
            if(f) dis+=w*W;
            else dis+=w;
            if(d[j]>dis){
                d[j]=dis;
                pre[j]=u;
                q.push({d[j],j});
            }
        }
    }
    return d[n];
}
map<pii,int> mp;
int fun(){
    int cnt=0;
    for(int i=n;i;i=pre[i]) cnt+=mp[{pre[i],i}];
    return cnt;
}
void solve(){
    cin>>n>>m; 
    for(int i=1;i<=m;i++){
        int u,v,w; cin>>u>>v>>w;
        g[u].push_back({v,w,0});
        g[v].push_back({u,w,1});
        mp[{u,v}]=0;
        mp[{v,u}]=w;
    }
    int L=1;
    while(L<=1e5){
        int l=L,r=1e5; 
        int dis=dijkstra(l),sum=fun();
        while(l<r){
            int mid=(l+r+1)>>1;
            if(dijkstra(mid)==dis+(mid-L)*sum) l=mid;
            else r=mid-1;
        }
        for(int i=L;i<=l;i++) ans[i]=dis+(i-L)*sum;
        L=l+1;
    }
    int q; cin>>q;
    while(q--){
        int W; cin>>W;
        cout<<ans[W]<<endl;
    }
}
signed main(){
    IOS;
    int _=1;
    //cin>>_;
    while(_--){
        solve();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值