偏离路 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<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
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_queue<P,vector<P>,greater<P> >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].d<lalal[lalal[x].r].d) swap(lalal[x].l,lalal[x].r);
    lalal[x].d=lalal[lalal[x].r].d+1;
    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]<MAX) root[s[u].y]=Merge(root[s[u].y],bt(P(s[u].z-(d[s[u].y]-d[s[u].x]),s[u].x)));//注意这里的x和y,这里又要反过来啦,因为之前都是反向图 
    for (int u=1;u<=n;u++) dfs(u);
    priority_queue <P,vector<P>,greater<P> > 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值