本来A*就可以搞定的题,为了怕以后卡复杂度,找了个这么个方法

现阶段水平不够就不补充算法分析部分了

对于图G,建立一个以终点t为起点的最短路径构成的最短路径树
(就是反着跑一遍最短路,然后对于一个不为终点的点v,v到终点t的最短路径上(任选一条)v的后继结点为v的父亲,就形成了一棵树)
然后对于所有点,定义其不在最短路径树上的出边的f值为:f[e] = l[e] + dis[e.tail] - dis[e.head] ,就是走这条边,走到t需要多绕的距离
那么我们只要找到第k小的这种边的序列就得到解了
那么我们维护按权值一个从小到大的优先队列,每次从队头取出一个序列q,设q的最后一条边e的head为u,tail为v
我们可以选择在序列的末尾加上v到t的所有路径上非树边的最小的得到一个新的序列q1
或者选择u到t的所有路径上所有非树边中e的后继(没用过的边中最小的)替换e得到q2,将q1,q2都塞进优先队列,重复k次,
可是怎么才能尽快知道一个节点v到t的所有路径上的非树边最小的一个呢?
打个可持久化的可并堆就没问题了,每个点合并他到根的路径上所有非树出边,然后对于找e的后继替换e的操作,直接找e的两个孩子就行了

本题难度爆表,低级图论和高级数据结构的大综合

直接上代码了,以后学的多了再回过头来看方法

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=; //为啥开这么大???
const int maxm=;
const int INF=0x7fffffff;
int n,m,cnt,cntf,st,ed,k,tot,tp;
bool vi[maxn];
int g[maxn],gf[maxn],dis[maxn],_next[maxn],root[maxn],sta[maxn];
struct Edge
{
int u,v,w,f,next;
bool vis,flag;
}e[maxm];
struct Edgef
{
int t,w,next;
}ef[maxm]; void addedge(int x,int y,int z)
{
cnt++;
e[cnt].u=x;e[cnt].v=y;e[cnt].w=z;
e[cnt].next=g[x];g[x]=cnt;
e[cnt].vis=;
}
void addedgef(int x,int y,int z)
{
cntf++;
ef[cntf].t=y;ef[cntf].w=z;
ef[cntf].next=gf[x];gf[x]=cntf;
} struct Node
{
int lc,rc,dis,c,y;
}tr[maxn*];
int newnode(int c,int y)
{
tot++;
tr[tot].lc=tr[tot].rc=;
tr[tot].dis=;
tr[tot].c=c;
tr[tot].y=y;
return tot;
}
int merge(int x,int y)
{
//cout<<x<<" "<<y<<endl;
if(x==||y==) return x|y;
if(tr[x].c>tr[y].c) swap(x,y);
int ret=++tot;
tr[ret]=tr[x];
int k=merge(tr[ret].rc,y);
if(tr[tr[ret].lc].dis<=tr[k].dis) swap(tr[ret].lc,k);
tr[ret].rc=k;
tr[ret].dis=tr[tr[ret].lc].dis+;
return ret;
}
struct HeapNode
{
int x,d;
};
bool operator <(HeapNode x,HeapNode y)
{
return x.d>y.d;
}
priority_queue<HeapNode> q; struct Graph
{
int u,x,d;
};
bool operator < (Graph x,Graph y)
{
return x.d>y.d;
};
priority_queue<Graph> Q;
void getdis()
{
dis[ed]=;
HeapNode temp;
temp.x=ed;temp.d=;
q.push(temp);
while(!q.empty())
{
HeapNode x=q.top();q.pop();
if(dis[x.x]<x.d) continue;
for(int tmp=gf[x.x];tmp;tmp=ef[tmp].next)
{
int y=ef[tmp].t;vi[y]=;
if(dis[y]>x.d+ef[tmp].w)
{
dis[y]=x.d+ef[tmp].w;
temp.x=y;temp.d=dis[y];
q.push(temp);
}
}
}
}
void solve(int x)
{
if(x==ed)
{
for(int tmp=g[x];tmp;tmp=e[tmp].next)
{
int y=e[tmp].v;
if(e[tmp].flag==) continue;
if(e[tmp].vis==)
{
root[x]=merge(root[x],newnode(e[tmp].f,e[tmp].v));
}
}
return;
}
for(int tmp=g[x];tmp;tmp=e[tmp].next)
{
int y=e[tmp].v;
if(e[tmp].flag==) continue;
if(e[tmp].vis==)
root[x]=merge(root[x],newnode(e[tmp].f,e[tmp].v));
else root[x]=merge(root[x],root[y]);
}
}
int main()
{
int u,v,w;
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
e[cnt].flag=;
addedgef(v,u,w);
}
scanf("%d%d%d",&st,&ed,&k);
if(st==ed) k++; for(int i=;i<=n;i++)
dis[i]=INF,vi[i]=;
getdis(); if(k==)
{
if(vi[st]) printf("%d\n",dis[st]);
else printf("-1\n");
return ;
}
for(int i=;i<=cnt;i++)
{
e[i].f=e[i].w-dis[e[i].u]+dis[e[i].v];
if(dis[e[i].v]==INF) e[i].flag=;
}
for(int i=;i<=n;i++)
{
if(i==ed) continue;
for(int tmp=g[i];tmp;tmp=e[tmp].next)
{
v=e[tmp].v;
if(!e[tmp].flag) continue;
if(dis[i]==dis[v]+e[tmp].w)
{
e[tmp].vis=;
_next[i]=v;
break;
}
}
}
memset(root,,sizeof(root));
tot=;
for(int i=;i<=n;i++)
if(!root[i])
{
if(dis[i]==INF) continue;
sta[tp=]=i;
while()
{
u=sta[tp];
if(u==ed) break;
if(!root[_next[u]]) sta[++tp]=_next[u];
else break;
}
while(tp)
{
solve(sta[tp]);
tp--;
}
}
k-=;
Graph ss;
ss.u=st;ss.d=tr[root[st]].c;ss.x=root[st];
Q.push(ss);
while(k--)
{
Graph tmp=Q.top();Q.pop();
if(tmp.u==)
{
printf("-1\n");
return ;
}
if(tr[tmp.x].lc)
{
Graph tmp1;
tmp1.u=tmp.u;
tmp1.d=-tr[tmp.x].c;
tmp1.x=merge(tr[tmp.x].lc,tr[tmp.x].rc);
tmp1.d+=tr[tmp1.x].c+tmp.d;
Q.push(tmp1);
}
Graph tmp2;
tmp2.u=tr[tmp.x].y;
tmp2.d=tmp.d+tr[root[tmp2.u]].c;
tmp2.x=root[tmp2.u];
Q.push(tmp2);
}
Graph ans=Q.top();
if(ans.u==)
{
printf("-1\n");
return ;
}
if(vi[st]) printf("%d\n",dis[st]+ans.d);
else printf("-1\n");
return ;
}

200多行幸亏没出什么调不出来的错误,唉,菜啊

数据结构&图论:K短路-可持久化可并堆的更多相关文章

  1. luogu 2483 K短路 (可持久化左偏树)

    题面: 题目大意:给你一张有向图,求1到n的第k短路 $K$短路模板题 假设整个图的边集为$G$ 首先建出以点$n$为根的,沿反向边跑的最短路树,设这些边构成了边集$T$ 那么每个点沿着树边走到点$n ...

  2. 图论(A*算法,K短路) :POJ 2449 Remmarguts' Date

    Remmarguts' Date Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 25216   Accepted: 6882 ...

  3. 图论&搜索:K短路-启发式搜索

    判断第k短路的权值是否小于T 直接把队友的代码拿过来了,一定很经典 #include <iostream> #include <queue> #include <cstr ...

  4. 与图论的邂逅07:K短路

    在做最短路的题时我们不免会碰到许多求次短路的题,然而我们也能很快地想到解决的办法: 用dijkstra跑一遍最短路,当终点第二次被取出时就是次短路了.时间复杂度为O((N+M)logN).实际上前面得 ...

  5. 浅谈k短路算法

    An Old but Classic Problem 给定一个$n$个点,$m$条边的带正权有向图.给定$s$和$t$,询问$s$到$t$的所有权和为正路径中,第$k$短的长度. Notice 定义两 ...

  6. bzoj 1598: [Usaco2008 Mar]牛跑步 [k短路 A*] [学习笔记]

    1598: [Usaco2008 Mar]牛跑步 题意:k短路 ~~貌似A*的题目除了x数码就是k短路~~ \[ f(x) = g(x) + h(x) \] \(g(x)\)为到达当前状态实际代价,\ ...

  7. K短路 (A*算法) [Usaco2008 Mar]牛跑步&[Sdoi2010]魔法猪学院

    A*属于搜索的一种,启发式搜索,即:每次搜索时加一个估价函数 这个算法可以用来解决K短路问题,常用的估价函数是:已经走过的距离+期望上最短的距离 通常和Dijkstra一起解决K短路 BZOJ1598 ...

  8. 算法笔记--次小生成树 && 次短路 && k 短路

    1.次小生成树 非严格次小生成树:边权和小于等于最小生成树的边权和 严格次小生成树:    边权和小于最小生成树的边权和 算法:先建好最小生成树,然后对于每条不在最小生成树上的边(u,v,w)如果我们 ...

  9. P2483 【模板】k短路([SDOI2010]魔法猪学院)

    题目背景 感谢@kczno1 @X_o_r 提供hack数据 题目描述 iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界 ...

随机推荐

  1. Vuex实践

    本文来自网易云社区 作者:刘凌阳 前言 2017年对于Vue注定是不平凡的一年.凭借着自身简介.轻量.快速等特点,Vue俨然成为最火的前端MVVM开发框架.随着Vue2.0的release,越来越多的 ...

  2. 使用polarssl进行RSA加密解密

    RSA算法的原理就不提了,网上有很多介绍的文章,因为项目中使用RSA加密,所以需要找一个RSA加密的算法,之前尝试过使用Crypto++库,无奈Crypto++其中使用了大量的模版,各种继承,看着头大 ...

  3. spring多个定时任务quartz配置

    spring多个定时任务quartz配置 <?xml version=”1.0″ encoding=”UTF-8″?> <beans xmlns=”http://www.spring ...

  4. python--基础篇二

    一. 格式化输出 :name=input("name:") age=input("age:") hobby=input("hobbie:") ...

  5. GraphSAGE 代码解析 - minibatch.py

    class EdgeMinibatchIterator """ This minibatch iterator iterates over batches of samp ...

  6. mysql数据备份和还原

    MySQL是一个永久存储数据的数据库服务器.如果使用MySQLServer,那么需要创建数据库备份以便从崩溃中恢复.mysql提供了一个用于备份的实用程序mysqldump. 1.普通.sql文件中的 ...

  7. 在Android Studio中创建(或添加)第一个Hello World应用程序

    下面我们将使用Android Studio创建第一个简单的Hello World应用程序. 1.打开Android Studio,加载画面如下图所示:   2.选择”Start a new Andro ...

  8. 最小生成树(MST)

    原创 今天来说说最小生成树问题,我们知道最小生成树有两种求法,一种是prim算法,另一种是kruskal算法,关于两种算法的定义以及证明,请查看相关资料,这里不多说,理解起来也相当容易,我们来看一个问 ...

  9. 先查出已知的 然后将未知的当做 having里面的条件

    先查出已知的 然后将未知的当做 having里面的条件

  10. BZOJ4584 APIO2016赛艇(动态规划+组合数学)

    如果值域不大,容易想到设f[i][j]为第i个学校选了j的方案数,枚举上一个学校是哪个选了啥即可,可以前缀和优化.于是考虑离散化,由于离散化后相同的数可能可以取不同的值,所以枚举第一个和其所选数(离散 ...