[K短路 可持久化堆 最短路径树] JDFZ 2978 第k短路(强)

本文介绍了鼎爷关于可持久化堆和K短路的课程内容,重点是解冑JDFZ在线判题平台上的第2978题。通过链接可以访问题目详情,学习如何利用可持久化堆解决最短路径树问题。

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

链接:https://oj.jdfz.com.cn/oldoj/problem.php?id=2978

鼎爷的【课件】堆的可持久化和k短路











其中每条边的新权值可以形象的理解为走这条边要多花的代价
然后这个东西怎么维护呢 
我们维护一个堆存储sidetracks 初始放入空序列表示最短路
每次取出堆顶 last表示序列最后一条边e的to 空序列则last为S
这个序列的前驱的last记为u
可以选择在序列的末尾加上last到T的在最短路径树所有点连出的非树边的最小的的一条边
或者选择u到T的在最短路径树所有点连出的非树边中e的后继(没用过的边中最小的)替换e
然后都塞进堆里

怎么维护last到T的在最短路径树所有点连出的非树边的最小的的一条边
在最短路径树上搞一个可持久化堆 那么加边很轻易
替换只需要换成e在堆中的两个儿子就行了

记得对于不能到达T的边要删掉
O( n log n + m log m + k log k ) 


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#define cl(x) memset(x,0,sizeof(x));
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int M=100005;  
const int N=10005;

struct edge{  
  int u,v,next; ll w;  
}G[M];  
int head[N],inum=1;

inline void add(int u,int v,ll w,int p){  
  G[p].u=u; G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p;  
}

#define V G[p].v
#define U G[p].u

namespace DIJ{  
#define val(x) dis[H[x]]  
#define K(x) (x<<1|1)>cnt?(x<<1):(val(x<<1)<val(x<<1|1)?(x<<1):(x<<1|1))  
  ll* dis;  
  int H[N];  
  int cnt,back[N],vst[N];  
  inline void Swp(int x,int y){  
    swap(H[x],H[y]); swap(back[H[x]],back[H[y]]);  
  }  
  inline void Up(int x){  
    for (;x && val(x)<val(x>>1);x>>=1) Swp(x,x>>1);            
  }  
  inline void Down(int x){  
    for (int tmp;(x<<1)<=cnt && val(x)>val(tmp=K(x));x=tmp) Swp(x,tmp);  
  }  
  inline void Dij(int S,int n,ll *dis){  
    int u; DIJ::dis=dis;  
    cnt=0; H[++cnt]=S; back[S]=cnt; dis[S]=0; vst[S]=1;  
    for (int i=1;i<=n;i++)  
      if (i!=S)  
	H[++cnt]=i,back[i]=cnt,dis[i]=1<<30,vst[i]=0;  
    for (int i=1;i<n;i++){  
      u=H[1]; vst[H[1]]=1;  
      Swp(1,cnt--); Down(1);  
      for (int p=head[u];p;p=G[p].next)  
	if (!vst[V] && dis[V]>dis[U]+G[p].w){  
	  dis[V]=dis[U]+G[p].w;  
	  Up(back[V]);  
	}  
    }  
  }  
}

inline int ran(){
  static int x=31253125; x+=(x<<4)+1; return x&65536;
}

struct node{
  node *l,*r; int p;
  bool operator < (const node &B) const{
    return G[p].w<G[B.p].w;
  }
}nodes[(N+M)*30],*root[N];
int ncnt,rcnt;

inline node *Me(node *A,node *B){
  node *ret=nodes+(++ncnt);
  if (!A || !B) { *ret=A?*A:*B; return ret; }
  if (*B<*A) swap(A,B);
  *ret=*A; ran()?ret->l=Me(A->l,B):ret->r=Me(A->r,B);
  return ret;
}

int S,T;
int n,m,K;
int ru[M],rv[M],rw[M];
ll dis[N]; int vst[N],nxt[N];
int tag[M];

int lst[N],pnt;

inline void dfs(int u){
  vst[u]=1; lst[++pnt]=u;
  for (int p=head[u];p;p=G[p].next)
    if (!vst[V] && dis[V]==dis[u]+G[p].w)
      tag[p]=1,nxt[V]=u,dfs(V);
}

inline void Build(){
  for (int i=1;i<=pnt;i++){
    int u=lst[i];
    node *last=root[nxt[u]];
    for (int p=head[u];p;p=G[p].next)
      if (!tag[p] && dis[V]!=1<<30){
	++ncnt; nodes[ncnt].p=p;
	last=Me(last,nodes+ncnt);
      }
    root[u]=last;    
  }
}

struct abcd{
  ll val; node *last;
  abcd(ll v,node *l) { val=v; last=l; }
  bool operator < (const abcd &B) const{
    return val>B.val;
  }
};

priority_queue<abcd> Q;

ll Ans;
  
inline void Solve(){
  Q.push(abcd(dis[S],NULL));
  for (int i=1;i<=K;i++){
    abcd t=Q.top(); Q.pop();
    if (i==K) { Ans=t.val; break; }
    int v=t.last?G[t.last->p].v:S;
    if (t.last){
      if (t.last->l)
	Q.push(abcd(t.val-G[t.last->p].w+G[t.last->l->p].w,t.last->l));
      if (t.last->r)
	Q.push(abcd(t.val-G[t.last->p].w+G[t.last->r->p].w,t.last->r));
    }
    if (root[v])
      Q.push(abcd(t.val+G[root[v]->p].w,root[v]));
  }
}

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m); read(K);
  for (int i=1;i<=m;i++) read(ru[i]),read(rv[i]),read(rw[i]),add(rv[i],ru[i],rw[i],++inum);
  S=1; DIJ::Dij(T=n,n,dis);
  dfs(T);
  cl(head); inum=1;
  for (int i=1;i<=m;i++) add(ru[i],rv[i],dis[rv[i]]+rw[i]-dis[ru[i]],++inum);
  Build();
  Solve();
  printf("%lld\n",Ans);
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值