洛谷p2483 模板k短路 可持久化可并堆

俞鼎力大牛:堆得可持久化

直接上代码?

#include<bits/stdc++.h>
#define eps 1e-8
using namespace std;
int head[10005],cnt=-1,v[10005],rt[10005],mark[1000005],ans,fa[10005];
int n,m,tot;
double se,dis[10100];
priority_queue<pair<double,int > >q;//这里注意,是double ,int 
struct Heap{
    int ls,rs,dist,to;//左儿子 右儿子 左偏树内点的dist,每个点对应边的终点  
    double w;//小根堆每个点的权值 
}tr[30000010];
struct node{
    int next,to;
    double w;
}e[10000010];
void add(int from,int to,double w){
    e[++cnt].next=head[from];
    e[cnt].to=to;
    e[cnt].w=w;
    head[from]=cnt;
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(tr[x].w-tr[y].w>=eps)swap(x,y);
    int p=++tot;
    tr[p]=tr[x];
    tr[p].rs=merge(tr[p].rs,y);
    if(tr[tr[p].ls].dist<tr[tr[p].rs].dist)swap(tr[p].ls,tr[p].rs);
    tr[p].dist=tr[tr[x].rs].dist+1;
    return p;
}
void dijkstra(){
    memset(v,0,sizeof v);
    memset(dis,127,sizeof dis);
    dis[n]=0;
    q.push(make_pair(0,n));
    while(!q.empty()){
        int from=q.top().second;
        q.pop();
        if(v[from])continue;
        v[from]=1;
        for(int i=head[from];~i;i=e[i].next){
            if(i&1){//反向边 
                int to=e[i].to;
                if(dis[to]-(dis[from]+e[i].w)>eps){
                    dis[to]=dis[from]+e[i].w;
                    q.push(make_pair(-dis[to],to));
                }
            }
        }
    }
}//djk跑一遍从终点反向往回走的最短路树 
void dfs(int from){
    v[from]=1;
    for(int i=head[from];i!=-1;i=e[i].next){
        int to=e[i].to;
        if(i&1){
            if(v[to])continue;
            if(fabs(dis[from]+e[i].w-dis[to])<eps){
                fa[to]=from;
                mark[i^1]=1;
                dfs(to);
            }
        }
    }
}//标记一下最短路树上有哪些边
int newnode(double w,int to){
    int x=++tot;
    tr[x].w=w;
    tr[x].dist=1;
    tr[x].to=to;
    return x;
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d%lf",&n,&m,&se);
    for(int i=1;i<=m;i++){
        int x,y;
        double z;
        scanf("%d%d%lf",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dijkstra();
    memset(v,0,sizeof v);
    dfs(n);
    for(int i=0;i<=cnt;i+=2){
        if(!mark[i]){
            int from=e[i^1].to,to=e[i].to;
            if(dis[from]==dis[0]||dis[to]==dis[0])continue;
            rt[from]=merge(rt[from],newnode(dis[to]+e[i].w-dis[from],to));
        }
    }//把不在最短路树上的路径加入堆;
    //权值是"走不在最短路树上的边的路径长度与最短路的路径长度的差值"
    for(int i=1;i<=n;i++){
        q.push(make_pair(-dis[i],i));
    } 
    for(int i=1;i<=n;i++){
        int from=q.top().second;
        q.pop();
        if(fa[from])rt[from]=merge(rt[from],rt[fa[from]]);
    }//把在最短路树上的路径加入堆 
    if(dis[1]-se<eps)se-=dis[1],ans++; //走最短路的情况
    if(rt[1])q.push(make_pair(-tr[rt[1]].w,rt[1]));//假如rt[1]没有说明从n根本到不了1
    while(!q.empty()){
        int from=q.top().second;
        double cur=q.top().first; 
        double w=dis[1]-cur;//当前路径的长度
        if(w-se>=eps)break;//能量耗完
        q.pop();
        se-=w;
        ans++;//找到一条k路径 
        for(int i=0;i<2;i++){//加当前的堆的路径 
            int to=i?tr[from].rs:tr[from].ls;
            if(to)q.push(make_pair(cur+tr[from].w-tr[to].w,to));//一条新的路径 加入优先队列 
        }//把左儿子和右儿子加入优先队列,因为是小根堆 ,所以这样加肯定是从小到大的; 
        if(rt[tr[from].to])q.push(make_pair(cur-tr[rt[tr[from].to]].w,rt[tr[from].to])); //加to对应的新的堆的路径; 
    }
    printf("%d\n",ans);
}

 

 

posted @ 2019-11-03 17:19  passione  阅读(346)  评论(0编辑  收藏  举报