1 条题解

  • 0
    @ 2025-8-24 22:25:29

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Youth518
    青春就是疯狂地奔跑,然后华丽地跌倒

    搬运于2025-08-24 22:25:29,当前版本为作者最后更新于2021-02-03 23:14:29,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    分析:

    前排先 stO 一下题面里的 Karry5307\color{black}{K}\color{red}{arry5307}

    对于这个题来说,我们发现没有办法直接找到每条路径第 kk 大的边是谁,所以我们考虑直接钦定每一条边是第 kk 大的情况

    假设我们钦定一条边权为 ww 的边为第 kk 大的边,那么所有小于 ww 的边边权变为 00 ,大于等于 ww 的边权变为 xwx-w ,这样跑完最短路后的代价就是 ans=disn+kwans=dis_n+k*w

    但是我们发现,这样做的话可能存在三种情况

    1. ww 恰好为此时最短路上第 kk 大的边,不影响

    2. ww 为此时最短路上小于第 k (k<k)k'\ (k'<k) 大的边,那么我们求得的答案一定大于实际情况

      证明:因为这种情况下,我们得到的答案等价于将第 11 大到第 kk' 大边的边权不变,但第 k+1k'+1 到第 kk 大的边的边权变大为 ww ,所以答案偏大

    3. ww 为此时最短路上大于第 k (k>k)k'\ (k'>k) 大的边,那么我们求得的答案还是大于实际情况的,因为有一些原本该变成 00 的边没有变成 00

    由此看来我们枚举边得到的答案是不小于实际答案的,那么我们只需要钦定每一条边,然后对所有的答案取最小值那么一定会得到恰好第 kk 大的情况,复杂度 O(m2log)O(m^2\log )

    tip:

    可能存在一种情况就是不钦定任何边的情况下,原图的最短路小于 kk 条边,那么这种情况直接是最优解,因为至少需要付前 kk 条边的费用,所以最后的答案还要和这种情况也取一下 min\min

    代码:

    #include<bits/stdc++.h>
    #define inl inline
    #define reg register
    #define pll pair<long long,long long>
    #define mk(x,y) make_pair(x,y)
    #define fir first
    #define sec second
    
    using namespace std;
    
    namespace zzc
    {
        typedef long long ll;
        inl ll read()
        {
            ll x=0,f=1;char ch=getchar();
            while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
            while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
            return x*f;
        }
    
        const ll maxn = 3005;
        const ll inf = 0x3f3f3f3f3f3f3f3f;
        ll n,m,k,cnt,ans;
        ll head[maxn],dis[maxn];
        bool vis[maxn];
        priority_queue<pll> q;
    
        struct edge
        {
            ll to,nxt,w;
        }e[maxn<<1];
    
        inl void add(ll u,ll v,ll w)
        {
            e[++cnt].to=v;
            e[cnt].w=w;
            e[cnt].nxt=head[u];
            head[u]=cnt;
        }
        
        inl void dijkstra(ll x)//x 表示我们钦定的第 k 大的边的边权
        {
            memset(dis,0x3f,sizeof(dis));
            memset(vis,false,sizeof(vis));
            dis[1]=0;q.push(mk(0,1));
            while(!q.empty())
            {
                ll u=q.top().sec;q.pop();
                if(vis[u]) continue;
                vis[u]=true;
                for(reg ll i=head[u];i;i=e[i].nxt)
                {
                    ll v=e[i].to;
                    ll w=max(0ll,e[i].w-x);//记得要和 0 取 max
                    if(dis[v]>dis[u]+w)
                    {
                        dis[v]=dis[u]+w;
                        q.push(mk(-dis[v],v));
                    }
                }
            }
             
        }
    
        void work()
        {
            ll a,b,c;
            n=read();m=read();k=read();
            for(reg ll i=1;i<=m;i++)
            {
                a=read();b=read();c=read();
                add(a,b,c);add(b,a,c);
            }
            dijkstra(0);ans=dis[n];//tip 里提到的情况
            for(ll i=1;i<=(m<<1);i+=2)
            {
                dijkstra(e[i].w);
                ans=min(ans,dis[n]+k*e[i].w);
            }
            printf("%lld\n",ans);
        }
    
    
    }
    
    int main()
    {
        zzc::work();
        return 0;
    }
    
    • 1

    [NEERC 2017] Journey from Petersburg to Moscow

    信息

    ID
    6120
    时间
    3000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者