2019CCPC网络赛 HDU6705 - path(图论,优先队列)

链接HDU6705 - path

题意:

给出一个带边权有向图,含有 n n n个结点 m m m条边,共 q q q次询问,每次询问在所有路径中第 k k k小的路径边权和是多少?(一条边可以走无限次)

  • ( 1 ≤ n , m , q , k ≤ 5 ∗ 1 0 4 ) (1≤n,m,q,k≤5∗10^4) (1n,m,q,k5104)


分析:

每次 把边权和尽量小的路径状态 放入优先队列中,每次从队首取出当前最小路径(即第 i i i小),利用此路径状态 找到接下来尽量小的路径状态

先将所有结点的 出边按照边权从小到大排序,将所有点 最小出边 放入优先队列中。

假设一条路径是 u → v u\rarr v uv结尾,其 路径和为 s u m sum sum,且 u → v u\rarr v uv u u u的所有出边中边权第 c u r cur cur 的,该路径下一条可能的尽量小路径有 2 2 2种情况:

  1. v v v结点开始,走其最小出边,则令 s u m + g [ v ] m i n sum+g[v]_{min} sum+g[v]min
  2. u u u结点开始,走其 下一条边权较大于 u → v u\rarr v uv的出边(即第 c u r + 1 cur+1 cur+1小的出边),则令 s u m − g [ u ] [ c u r ] + g [ u ] [ c u r + 1 ] sum-g[u][cur]+g[u][cur+1] sumg[u][cur]+g[u][cur+1]

所以需要记录的路径状态有: u , v , c u r , s u m u,v,cur,sum u,v,cur,sum



以下代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=5e5+50;
int n,m,q;
LL ans[maxn];
struct edge
{
    int to;
    LL w;
};
vector<edge> g[maxn];
bool cmp(const edge &x,const edge &y)
{
    return x.w<y.w;
}
struct node
{
    int u,v;
    int cur;
    LL sum;
    friend bool operator < (const node &x,const node &y)
    {
        return x.sum>y.sum;
    }
};
void init()
{
    for(int i=1;i<=n;i++)
        g[i].clear();
}
void solve()
{
    for(int i=1;i<=n;i++)
        sort(g[i].begin(),g[i].end(),cmp);
    priority_queue<node> q;
    for(int i=1;i<=n;i++)
    {
        if(!g[i].empty())
            q.push(node{i,g[i][0].to,0,g[i][0].w});
    }
    for(int i=1;i<=50000;i++)
    {
        int u=q.top().u,v=q.top().v,cur=q.top().cur;
        LL sum=q.top().sum;
        q.pop();
        ans[i]=sum;
        if(!g[v].empty())
            q.push(node{v,g[v][0].to,0,sum+g[v][0].w});
        if(cur+1<g[u].size())
            q.push(node{u,g[u][cur+1].to,cur+1,sum-g[u][cur].w+g[u][cur+1].w});
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d %d",&n,&m,&q);
        init();
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d %d %d",&u,&v,&w);
            g[u].push_back(edge{v,w});
        }
        solve();
        while(q--)
        {
            int k;
            scanf("%d",&k);
            printf("%lld\n",ans[k]);
        }
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值