突然觉得堆优化$O(log_n)$的复杂度很优啊,然而第n次忘记了$Dijistra$怎么写QAQ发现之前都是用的手写堆,这次用一下$stl$

#include<bits/stdc++.h>
#define LL long long
using namespace std; int n, m; struct Node {
int v, nex, w;
Node(int v = , int nex = , int w = ) :
v(v), nex(nex), w(w) { }
} Edge[]; int h[], stot;
void add(int u, int v, int w) {
Edge[++stot] = Node(v, h[u], w);
h[u] = stot;
} struct QAQ {
int u; LL dis;
QAQ(int u = , LL dis = ) :
u(u), dis(dis) { }
bool operator < (const QAQ a) const {
return dis > a.dis;
}
}; LL dis[];
bool flag[];
void Diji(int s) {
priority_queue < QAQ > q;
for(int i = ; i <= n; i ++) dis[i] = 0x3f3f3f3f;
dis[s] = ;
q.push(QAQ(s, ));
while(!q.empty()) {
QAQ x = q.top(); q.pop();
int u = x.u;
if(flag[u]) continue;
flag[u] = ;
for(int i = h[u]; i; i = Edge[i].nex) {
int v = Edge[i].v;
if(!flag[v] && dis[v] > dis[u] + Edge[i].w) {
dis[v] = dis[u] + Edge[i].w;
q.push(QAQ(v, dis[v]));
}
}
}
} int main() {
int s;
scanf("%d%d%d", &n, &m, &s);
for(int i = ; i <= m; i ++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
Diji(s);
for(int i = ; i <= n; i ++)
printf("%lld ", dis[i]);
return ;
}

然而遇到了这道题...

3040: 最短路(road)

Time Limit: 60 Sec  Memory Limit: 200 MB
Submit: 4331  Solved: 1387
[Submit][Status][Discuss]

Description

N个点,M条边的有向图,求点1到点N的最短路(保证存在)。
1<=N<=1000000,1<=M<=10000000

Input

第一行两个整数N、M,表示点数和边数。
第二行六个整数T、rxa、rxc、rya、ryc、rp。

前T条边采用如下方式生成:
1.初始化x=y=z=0。
2.重复以下过程T次:
x=(x*rxa+rxc)%rp;
y=(y*rya+ryc)%rp;
a=min(x%n+1,y%n+1);
b=max(y%n+1,y%n+1);
则有一条从a到b的,长度为1e8-100*a的有向边。

后M-T条边采用读入方式:
接下来M-T行每行三个整数x,y,z,表示一条从x到y长度为z的有向边。

1<=x,y<=N,0<z,rxa,rxc,rya,ryc,rp<2^31

Output

一个整数,表示1~N的最短路。

Sample Input

3 3
0 1 2 3 5 7
1 2 1
1 3 3
2 3 1

Sample Output

2

HINT

【注释】

请采用高效的堆来优化Dijkstra算法。

Source

[Submit][Status][Discuss]

HOME Back


普通的堆优化会$MLE$,因为优先队列会把一个点弄进去很多次。

在网上发现了配对堆这个东西,可以包含优先队列的所有操作!!$tql$!!!


以下转载大米饼的博客:

[产品特色]

      ①沛堆堆(乱取的绰号)是一颗多叉树。

      ②包含Priority_Queue的所有功能,可用于优化最短路。

      ③属于可并堆,因此对于集合合并维护最值的问题很实用。

      ④速度快于一般的堆结构(左偏树,斜堆,随机堆……),具体快在这里:

      这里就顺带引出它的基本操作啦:

        ·合并(Merge): O(1)

        ·插入(Insert/Push): O(1)

        ·修改值(Change): O(1)/O(logn)

        ·取出维护的最值(Top): O(1)

        ·弹出堆顶元素(Pop): O(logn)

[功能介绍]

       ①可并堆的灵魂——Merge操作

      这里令人惊奇的是,配对堆只需要O(1)的时间完成这一步,具体做法为比较两个需要合并的堆的根的权值大小,然后就将那优先级较低(比如你要求大的在堆顶,那么权值越大,优先级越高)置为另一个点的儿子,即fa[v]=u,再将u向v建边即可。

       ②经典操作之一——插入(Push/Insert)操作

就很容易了,将插入元素新建为一个单独的节点作为一个堆,与当前的堆进行Merge操作就可以了。

       ③经典操作之二——取最值(Top)操作

       就直接用Root记录堆根,然后返回val[Root]就美妙完成任务。

       ④重要而具有特色的操作——修改操作(Change)

       修改一个节点的的权值,那么怎么处理来继续保持配对堆的堆性质?首先将这个点和父节点的连边断掉,即fa[u]=0(由于父节点连边使用链式前向星,不方便删除,就不删除,但是这样并不会影响正确性,因为后文枚举一个点的儿子节点时,要确认某个点是它的儿子节点,不仅是要这个点能够有边指向这个儿子,同时需要这个儿子的fa[]中存储的就是这个节点)。

       断掉与父亲的连边后,相当于形成两个堆,接下来进行一次Merge操作就好了。可以发现这个操作的时间复杂度是O(1),但有资料认为这个操作可能会破坏配对堆应有的结构(这"应有"的结构在下文会体现出来,它是Pop操作是O(logn)而不是O(n)的重要保证),结构改变后就会影响Pop的复杂度,使其向 O(n)退化,因此计算后认定其实修改操作从时间复杂度贡献分析来看,可能是O(logn)而不是O(1)。

       ⑤最缓慢但很重要的操作——弹出最值(Pop)操作

            你会发现上文的操作都那么偷懒,几乎都是胡乱Merge一下,Merge函数又是随随便便连一条边就完事儿了……因此这个操作需要来收拾这个烂摊子。我们现在的任务是删除根节点,那么我们就要从它的儿子中选出合法继承人。如果直接将所有儿子挨个挨个Merge起来,那么这样很容易使得一个点有很多个儿子,从而影响后来的Pop操作时间,将O(logn)退化为O(n)。较快的做法是将子树两两合并,不断这样合并,最终形成一棵树,同理,这样之所以快是因为保证了后面pop操作时候点的儿子个数不会太多。

[要点尝鲜]

   ①链式前向星建边:

②Merge操作:

   ③Insert/Push操作:

   ④ChangeVal操作:

   ⑤Top操作:

   ⑥Pop操作:

[产品代码]

      接下来一份简洁的代码,内容是将n个数排序。

      其中的Stack是用来回收空间的。这里没有给出ChangVal函数,原因是这个函数适用于有特定位置的元素的修改,比如将数组插入堆,然后修改数组下表为i的元素权值。上文内容毫无保留地讲述了ChangVal的内容,直接打就是了。

       同样的,如果要用来维护一些信息,比如Dijkstra的优化,那就在点的信息上添加记录最短路中点的编号之类的形成映射以达成快速取值的目的,其实呢和STL优先队列是一样的。

#include<stdio.h>
#define go(i,a,b) for(int i=a;i<=b;i++)
#define fo(i,a,x) for(int i=a[x],v=e[i].v;i;i=e[i].next,v=e[i].v)
const int N=;
int n,a[N],b[N]; struct Stack
{
int S[N],s=,k=;
int get(){return s?S[s--]:++k;}
void Save(int index){S[++s]=index;}
}Node,Edge; struct Pairing_Heap
{
int sz=,fa[N],head[N],k,val[N],Root;
int S[N],s;struct E{int v,next;}e[N]; void ADD(int u,int v){e[k=Edge.get()]=(E){v,head[u]};head[u]=k;}
int Merge(int u,int v){val[u]>val[v]?u^=v^=u^=v:;ADD(fa[v]=u,v);return u;}
void Push(int Val){int u=Node.get();val[u]=Val;Root=Root?Merge(Root,u):u;}
int Top(){return val[Root];} void Pop()
{
s=;fo(i,head,Root)Edge.Save(i),fa[v]==Root?fa[S[++s]=v]=:;
fa[Root]=head[Root]=;Node.Save(Root);Root=;
int p=;while(p<s){++p;if(p==s){Root=S[p];return;}
int u=S[p],v=S[++p];S[++s]=Merge(u,v);}
}
}q;
int main()
{
scanf("%d",&n);
go(i,,n)scanf("%d",a+i),q.Push(a[i]);
go(i,,n)printf("%d\n",q.Top()),q.Pop();return ;
}//Paul_Guderian

大米飘香的总结:

沛堆堆继承了斐波拉契堆的优秀操作复杂度,同时相比之下降低了空间复杂度和代码复杂度,这样优美高效的数据结构当然适合用在竞赛领域。如果谈到什么时候会用到沛堆堆,大米饼认为主要是两个方面——代替优先队列和代替常规的可并堆。常规可并堆如斜堆,随机堆和左偏树虽然代码更短,但是时间复杂度不够理想,再说了沛堆堆代码其实也很短的(这使得我们可以直接手写而不用冒风险去调用STL中的沛堆堆了)。最后这篇博文有一个小小的缺陷是,由于大米饼笨笨的,其实上文中ChangeVal是有局限的,其中修改值只能比原值小(如果越小优先级越高的话),因为如果修改为较大值,其操作就类似与Pop了,虽然个人认为时间复杂度由于都是O(logn)不会影响,但毕竟还没试验过,须谨慎使用。如果大米饼出错了或者出现冗余,希望来浏览的人加以指出批评。


然而本人并不打算学透,而且有stl的配对堆,所以背背代码吧~~

#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
#define LL long long
using namespace std;
using namespace __gnu_pbds; typedef __gnu_pbds::priority_queue < pair < LL, int > > heap;
heap::point_iterator id[]; int n, m; struct Node {
int v, nex, w;
Node(int v = , int nex = , int w = ) :
v(v), nex(nex), w(w) { }
} Edge[]; int h[], stot;
void add(int u, int v, int w) {
Edge[++stot] = Node(v, h[u], w);
h[u] = stot;
} LL dis[];
bool flag[];
void Diji() {
heap q;
for(int i = ; i <= n; i ++) dis[i] = 0x3f3f3f3f;
id[] = q.push(make_pair(, ));
while(!q.empty()) {
int x = q.top().second; q.pop();
if(x == n) break;
for(int i = h[x]; i; i = Edge[i].nex) {
int v = Edge[i].v;
if(dis[v] > dis[x] + Edge[i].w) {
dis[v] = dis[x] + Edge[i].w;
if(id[v] != ) q.modify(id[v], make_pair(-dis[v], v));
else id[v] = q.push(make_pair(-dis[v], v));
}
}
}
} int main() {
scanf("%d%d", &n, &m);
int T, rxa, rxc, rya, ryc, rp;
scanf("%d%d%d%d%d%d", &T, &rxa, &rxc, &rya, &ryc, &rp);
int x = , y = , z = ;
for(int i = ; i <= T; i ++) {
int a, b;
x = ((long long)x * rxa + rxc) % rp;
y = ((long long)y * rya + ryc) % rp;
a = min(x % n + , y % n + );
b = max(y % n + , y % n + );
add(a, b, 1e8 - * a);
}
for(int i = ; i <= m - T; i ++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
Diji();
printf("%lld", dis[n]);
return ;
}

【最短路Dijistra】【一般堆优化】【配对堆优化】的更多相关文章

  1. POJ 3635 - Full Tank? - [最短路变形][手写二叉堆优化Dijkstra][配对堆优化Dijkstra]

    题目链接:http://poj.org/problem?id=3635 题意题解等均参考:POJ 3635 - Full Tank? - [最短路变形][优先队列优化Dijkstra]. 一些口胡: ...

  2. 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)

    关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...

  3. dijkstra(最短路)和Prim(最小生成树)下的堆优化

    dijkstra(最短路)和Prim(最小生成树)下的堆优化 最小堆: down(i)[向下调整]:从第k层的点i开始向下操作,第k层的点与第k+1层的点(如果有)进行值大小的判断,如果父节点的值大于 ...

  4. 配对堆优化Dijkstra算法小记

    关于配对堆的一些小姿势: 1.配对堆是一颗多叉树. 2.包含优先队列的所有功能,可用于优化Dijkstra算法. 3.属于可并堆,因此对于集合合并维护最值的问题很实用. 4.速度快于一般的堆结构(左偏 ...

  5. 3424:Candies(差分约束,Dijkstra)(配对堆优化

    题面链接 题解 令x-y<=z表示x最大比y大z. 若b-a<=k1, c-b<=k2, c-a<=k3,那么c-a最大为多少呢?显然应该等于min(k1+k2, k3).可以 ...

  6. 《Windows核心编程系列》十四谈谈默认堆和自定义堆

    堆 前面我们说过堆非常适合分配大量的小型数据.使用堆可以让程序员专心解决手头的问题,而不必理会分配粒度和页面边界之类的事情.因此堆是管理链表和数的最佳方式.但是堆进行内存分配和释放时的速度比其他方式都 ...

  7. 堆和索引堆的实现(python)

    ''' 索引堆 ''' ''' 实现使用2个辅助数组来做.有点像dat.用哈希表来做修改不行,只是能找到这个索引,而需要change操作 还是需要自己手动写.所以只能用双数组实现. #引入索引堆的核心 ...

  8. Eclipse MAT:浅堆 vs 保留堆

    来自:唐尤华 https://dzone.com/articles/eclipse-mat-shallow-heap-retained-heap 有没有想要搞清楚浅堆(Shallow Heap)和保留 ...

  9. 十二、jdk工具之jcmd介绍(堆转储、堆分析、获取系统信息、查看堆外内存)

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

随机推荐

  1. 跳出python的各种坑(1)

    2017-11-1915:38:17 一定要跳出python的各种坑,一开始遇到的好多思维上的认知错误,因为刚开始学习,对python是个什么都不清楚,所以记录一下自己遇到的各种坑.不用担心自己遇到的 ...

  2. Linux 2440 LCD 控制器【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/01/14/2396864.html 嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux ...

  3. jmeter,测登录,要不要过滤掉JS,CSS等请求?感觉过滤掉了压出来的数据就不真实?

    首先,我们来明确下你的性能测试目的,你的目的是服务端的性能还是前端的性能.这两用目的所涉及到的测试场景和工具等方法是不一样的.1.我们先来谈谈服务端的性能.一般的web产品,像css, jpeg等这种 ...

  4. 遍历 USB devcie,读取设备描述符 device descriptor【转】

    转自:http://blog.csdn.net/flyyyri/article/details/5480347 理论:    对于USB接口的设备,现在越来越多了.本篇我们就通过获取一个USB扫描仪设 ...

  5. 【转】WCF光芒下的Web Service

    WCF光芒下的Web Service 学习.NET的开发人员,在WCF的光芒照耀下,Web Service 似乎快要被人遗忘了.因为身边做技术的人一开口就是WCF多么的牛逼!废话不多,本人很久不写博客 ...

  6. Awk基础

    Awk文本处理 awk是一种编程语言,用于在linux/unix下对文本和数据进行处理.awk数据可以来自标准输入.一个或多个文件,或其它命令的输出.awk通常是配合脚本进行使用, 是一个强大的文本处 ...

  7. vue2.0组件之间的传值

    1.父子组件--props props:需要注意的是,虽然的是单向的数据流,但是如果传递的是数组或是对象这样的引用类型,子组件数据变化,父组件的数据通也会变化 子组件代码 <template&g ...

  8. tomcat运行内存溢出问题

    tomcat 运行内存配置优化 /opt/apache-tomcat-7.0.65/bin/catalina.sh:JAVA_OPTS="$JAVA_OPTS -Dfile.encoding ...

  9. DedeCMS栏目页调用当前栏目名和上级栏目名

    在构建网页的时候,如果不想逐个写栏目列表页的标题,即列表页标题形式为:{field:seotitle/}_{dede:global.cfg_webname/},其中{field:seotitle/}为 ...

  10. virtualenv,virtualenvwrapper安装及使用

    1.安装 # 安装: (sudo) pip install virtualenv virtualenvwrapper # centos7下 pip install virtualenv virtual ...