偏离路 k小路模板 poj2449Remmarguts' Date

首先呢,前几天学了A*算法来弄k小路。。
但是我很快就发现A*实在是太慢太慢太慢了。。
完全是可以被卡到 O(nk)
又由于题目的需要,这样的复杂度已经无法满足我们的需求
于是就去学了另外的方法,但由于网上的资料实在是少之又少。。
于是我就去找了一下大牛
感谢Claris大佬!百忙之间抽空给了我论文与他的模板!!非常感谢!!
论文名字是《俞鼎力-堆的可持久化与k短路》,想学的朋友可以去看看
这样的话时间复杂度就变成 O(nlogn+mlogm+klogk) 了,比之前不知道高到哪里去了。。

说实话,学了两天,其实还是有一点点迷。。
所以详细的解释就不写了吧。。
随便讲几句吧:
1.我们的可持久化堆维护的是这个点到终点所有非树边的“贡献”大小
2.我觉得啊,下文中

s[u].z-(d[s[u].y]-d[s[u].x])

这句话为什么这么写呢,那是因为,我们可以吧d[s[u].y]-d[s[u].x]等价于之前有一条x到y的边,这个你可以感性地认识,然后呢,你选了这条边,其实就相当于代价增加了这么多
3.我有一个坑。。晚点填

怕误导大家

在这里贴一个我的(Claris的)板子,至少我觉得有了板子学习会简单很多

#include
#include
#include
#include
#include
using namespace std;
typedef pair<int,int>P;
const int N=1005;
const int M=100010;
const int MAX=1<<30;
int n,m;
struct qq{int x,y,z,last;}s[M];int last[N];
void init (int x,int y,int z)
{
    s[++m].x=x;s[m].y=y;s[m].z=z;
    s[m].last=last[x];
    last[x]=m;
}
int S,T,K;
int d[N],f[N];//从这里到终点的最短距离,其实就是在反向图中终点到这个点的距离     这个点是从哪条边过来的 
void get_dis ()
{
    int x;
    priority_queuevector

,greater

>q; for (int u=1;u<=n;u++) d[u]=MAX,f[u]=0; q.push(P(d[T]=0,T)); while (!q.empty()) { P t=q.top();q.pop(); if (t.first>d[x=t.second]) continue; for (int u=last[x];u!=-1;u=s[u].last) { int y=s[u].y; if (d[y]>d[x]+s[u].z) { f[y]=u; q.push(P(d[y]=d[x]+s[u].z,y)); } } } } bool ok[M];//这条边是不是一条树边 int root[N],tot;//这个堆的根 给到的编号 struct Node { int l,r,d;//左偏树的东西 P v;//比较的权值 Node() {} Node (int _l,int _r,int _d,P _v) { l=_l;r=_r;d=_d;v=_v; } }lalal[2000010];//堆 int bt (P b) { lalal[++tot]=Node(0,0,0,b); return tot; } int Merge (int a,int b) { if (a==0||b==0) return a+b; if (lalal[a].v>lalal[b].v) swap(a,b);//我要维护一个小跟堆 int x=++tot;//新开一个点,因为要多次合并 lalal[x]=lalal[a]; lalal[x].r=Merge(lalal[a].r,b); if (lalal[lalal[x].l].d1; return x; } bool vis[N]; void dfs (int x)//合并 { if (f[x]==0||vis[x]==true) return ; vis[x]=true; dfs(s[f[x]].x); root[x]=Merge(root[x],root[s[f[x]].x]); } int solve () { int x,y,z; int mm=m;m=0; while (mm--) scanf("%d%d%d",&x,&y,&z),init(y,x,z); //这里建的是反向图 scanf("%d%d%d",&S,&T,&K); if (S==T) K++;//这题坑的地方。。 get_dis(); if (d[S]==MAX) return -1; if (K==1) return d[S]; K--; for (int u=1;u<=m;u++) ok[u]=false; for (int u=1;u<=n;u++) vis[u]=false,ok[f[u]]=true,root[u]=0;lalal[0].d=-1; tot=0;for (int u=1;u<=m;u++) if (ok[u]==false&&d[s[u].x]//注意这里的x和y,这里又要反过来啦,因为之前都是反向图 for (int u=1;u<=n;u++) dfs(u); priority_queue vector

,greater

> q; int ans; y=root[S]; if (y!=0) q.push(P(d[S]+lalal[y].v.first,y)); while (!q.empty()&&K) { K--; P t=q.top();q.pop(); ans=t.first; x=t.second,y=lalal[x].l; if (y!=0) q.push(P(ans-lalal[x].v.first+lalal[y].v.first,y)); y=lalal[x].r; if (y!=0) q.push(P(ans-lalal[x].v.first+lalal[y].v.first,y)); y=root[lalal[x].v.second]; if (y!=0) q.push(P(ans+lalal[y].v.first,y)); } return K>0?-1:ans; } int main() { while (scanf("%d%d",&n,&m)!=EOF) { memset(last,-1,sizeof(last)); printf("%d\n",solve()); } return 0; }

你可能感兴趣的:(堆/左偏树,k短路)