JDFZ 2978 第K短路 可持久化堆+A*思想

题意:链接

方法:可持久化堆+A*思想。

解析:

原来的A*算法据说是与BFS同级?

所以K短路的话,裸A*是O(n*k)级别的?

而这道题

1<=n<=10000,1<=m<=100000,1<=k<=10000

所以裸A*是爆炸的。

考虑来优化。

这里运用的优化方法均为俞鼎力大牛在pdf里谈到的思想。

PDF链接

首先,对于整个图,我们求一个从各个点到n的最短路径树。

然后呢现在重新定义一个边的权值。

(注意我是求各个点到n的最短路径树,所以边是反向的)

定义一个边的权值为该边的to的最短路径值减去该边的from的最短路径值,加上该边的边权。

其实说白了这是指走该边会绕的距离?

如此定义之后,我们把所有边的值给重新赋上。

然后怎么做呢。

第一次的绕道距离显然是0。

在每一个状态上开一个堆。

一个状态有两部分。

第一部分是已经绕道多少距离,第二部分是可以转移到哪些状态。

则我们开一个全局小根堆,键值是某状态已经绕道的距离加上该状态下的堆中的最小绕道距离。

然后显然每一次我们拿这个小根堆的堆顶。

并且将能够转移到的状态扔到小根堆内。

这样重复k次,便可以算出第k短路上绕道了多少距离,最后加上最短距离即可。

现在发现,对于每一个转移到的点来说,下次能转移的边是其到祖先的所有点可以转移的边。

所以这个部分我们只要建出来树出来乱搞记录一下即可。

然后这涉及到一个问题,就是每个点附带的堆合并的时候,我们要保留合并前的两个堆,所以需要用到可持久化。

什么你说空间开多大?

算不明白,所以蒟蒻开指针了。

其实这题与contest hunter 弱胜胡策 #round 1的T3类似。

具体可能我说的不太清楚,总之好好画画图即可写出代码。

至于复杂度。O(SPFA+mlogm+klogk)

另:为什么我老TM自带码长!受不了了!
大爷3个堆3000B,我特么两个堆4000+B!!!

代码:

#include "queue"
#include "cstdio"
#include "cstring"
#include "iostream"
#include "algorithm"
#define N 10010
#define M 100010
using namespace std;
long long fa[N];
long long v[N];
long long dis[N];
long long head[N];
long long headf[N];
long long on[M];
long long n,m,k;
struct line
{
    long long from,to,val;
}f[M];
long long cnt,cntf;
struct node
{
    long long from,to,val,next;
}edge[M],edgef[M];
struct Heap
{
    Heap *lson,*rson;
    long long val,to,h;
    Heap()
    {
        lson=rson=0x0,val=to=0,h=-1;
    }
}*heap[N];
Heap *merge(Heap *x,Heap *y)
{
    if(!x)return y;
    if(!y)return x;
    if(x->val>y->val)swap(x,y);
    x->rson=merge(x->rson,y);
    long long lh=-1,rh=-1;
    if(x->lson)lh=x->lson->h;
    if(x->rson)rh=x->rson->h;
    if(rh>lh)swap(x->lson,x->rson);
    x->h=rh+1;
    return x;
}
Heap *Merge(Heap *x,Heap *y)
{
    if(!x)return y;
    if(!y)return x;
    if(x->val>y->val)swap(x,y);
    Heap *ret=new Heap();
    *ret=*x;
    ret->rson=Merge(ret->rson,y);
    long long lh=-1,rh=-1;
    if(ret->lson)lh=ret->lson->h;
    if(ret->rson)rh=ret->rson->h;
    if(rh>lh)swap(ret->lson,ret->rson);
    ret->h=rh+1;
    return ret;
}
void init()
{
    memset(head,-1,sizeof(head));
    memset(headf,-1,sizeof(headf));
    cnt=cntf=1;
}
void edgeadd(long long from,long long to,long long val)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
void fadd(long long from,long long to,long long val)
{
    edgef[cntf].from=from,edgef[cntf].to=to,edgef[cntf].next=headf[from];
    edgef[cntf].val=val;
    headf[from]=cntf++;
}
void spfa(long long s)
{
    memset(dis,0x3f,sizeof(dis));
    memset(v,0,sizeof(v));
    queue<long long>q;
    q.push(s);
    v[s]=1;
    dis[s]=0;
    while(!q.empty())
    {
        long long u=q.front();
        q.pop();
        v[u]=0;
        for(long long i=head[u];i!=-1;i=edge[i].next)
        {
            long long to=edge[i].to;
            if(dis[u]+edge[i].val<dis[to])
            {
                dis[to]=dis[u]+edge[i].val;
                on[fa[to]]=0;
                fa[to]=i;
                on[fa[to]]=1;
                if(!v[to])
                {
                    v[to]=1;
                    q.push(to);
                }
            }
        }
    }
}
void Chaos_solve()
{
    memset(v,0,sizeof(v));
    queue<long long>q;
    q.push(n);
    v[n]=1;
    while(!q.empty())
    {
        long long u=q.front();
        q.pop();
        for(long long i=headf[u];i!=-1;i=edgef[i].next)
        {
            long long to=edgef[i].to;
            if(on[i]==0)
            {
                Heap *tmp=new Heap();
                tmp->val=edgef[i].val,tmp->to=edgef[i].to;
                heap[u]=Merge(heap[u],tmp);
            }else
            {
                heap[u]=Merge(heap[u],heap[to]);
            }
        }
        for(long long i=head[u];i!=-1;i=edge[i].next)
        {
            long long to=edge[i].to;
            if(v[to])continue;
            if(on[i])
            {
                q.push(to);
                v[to]=1;
            }   
        }
    }
} 
struct ele
{
    long long val;
    Heap *HEAP; 
};
bool operator < (ele a,ele b)
{
    return a.val+a.HEAP->val > b.val+b.HEAP->val;
}
priority_queue<ele>qq;
int main()
{
    init();
    scanf("%lld%lld%lld",&n,&m,&k);
    for(long long i=1;i<=m;i++)
    {
        long long x,y,z;
        scanf("%lld%lld%lld",&x,&y,&z);
        edgeadd(y,x,z);
    }
    spfa(n);
    for(long long i=1;i<=m;i++)
    {
        f[i].from=edge[i].to;
        f[i].to=edge[i].from;
        f[i].val=dis[edge[i].from]-dis[edge[i].to]+edge[i].val;
        fadd(f[i].from,f[i].to,f[i].val);
        edge[i].val=f[i].val;
    }
    Chaos_solve();
    ele tmp;
    tmp.val=0,tmp.HEAP=heap[1];
    qq.push(tmp);
    long long ans=0;
    for(long long i=2;i<=k;i++)
    {
        if(qq.empty())break;
        ele u=qq.top();
        qq.pop();
        ans=u.val+u.HEAP->val;
        ele noname;
        noname.val=u.val;
        noname.HEAP=Merge(u.HEAP->lson,u.HEAP->rson);
        if(noname.HEAP)qq.push(noname);
        noname.val=ans;
        noname.HEAP=heap[u.HEAP->to];
        if(noname.HEAP)qq.push(noname);
    }
    printf("%lld\n",ans+dis[1]);
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值