本来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多行幸亏没出什么调不出来的错误,唉,菜啊