题目大意:给定n个点m条边的图,求1~n的最短路 n<=100W m<=1000W
题目上说要用高效的堆来优化Dijkstra 于是我们自然而然就会想到斐波那契堆 但是那东西真的不是很好写 于是我们有很高效的替代品——Pairing-Heap(配对堆)
这东西真的很好写(除了手写栈以外,一个节点有多个儿子所以手写了栈)
首先Pairing-Heap有几个基本操作:
Merge(x,y) 将两堆合并 直接将权值比较大的点挂在较小的下面当做儿子即可
Insert(x) 插入一个点 直接将x与根节点合并
Decrease_Key(x) 将x的权值减小 如果x是根节点则无视 否则直接将x与父节点断开 然后将x与根节点合并
Pop() 删除根节点 将根节点的所有儿子都拎出来 从左到右两两合并 然后从右到左两两合并 反复如此直到只剩下一个儿子为止 然后将这个儿子作为新的根节点
其中除了Pop以外所有的操作都是O(1)的 而Pop操作可以证明是均摊O(logn) (我不会证)
对于Dijkstra算法来说,我们需要执行最多m次Decrease_Key 于是Decrease_Key操作优化到O(1)是十分有利的 也就是把Dijkstra的复杂度优化到了O(nlogn+m)
实现的时候由于网上很难找到相关代码 所以我第一次写的时候写挂了0.0…… 今天也是一顿神WA 才搞定0.0
对于每个节点 记录父亲节点 开一个栈记录所有的子节点 然后再开一个域记录键值
注意这里的栈不能用STL中的stack 因为stack在申请空间的时候默认申请五个变量 加上指针一共十倍 这十倍的空间足够让你MLE了
可以用vector代替栈或者干脆手写 我选择了手写QAQ
对于Merge(x,y)操作 设x为权值较小的节点 y为较大的 将y的父亲设为x 然后将x的儿子中插入y
对于Decrease_Key(x)操作 首先将x的父亲设为空 注意无需将x的父亲的儿子中删除x 然后将x与root合并
对于Pop()操作 首先开两个指针指向两个栈 然后对于根节点的每一个儿子 如果这个儿子的父亲是root 那么将这个儿子的父亲设为空 然后加入栈1
然后将栈1的节点两两配对 配对后的节点加入栈2 反复如此直到栈1中的元素不足两个 然后将栈1中的剩余元素(如果有)扔进栈2 交换两栈的指针
反复如此直到栈1中的元素只剩一个 此时将这个元素设为新的根节点 Pop()操作完成
于是Pairing-Heap的操作就完成了 加上Dijkstra的模板即可 不明白的地方详见代码
#include <stack> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 1000010 using namespace std; typedef long long ll; struct abcd{ int to,f,next; }table[M*6]; int head[M],tot; int n,m; ll X,Y,T,rxa,rxc,rya,ryc,rp; namespace IStream{ const int L=1<<15; char buffer[L]; char *S,*T; inline char Get_Char() { if(S==T) { T=(S=buffer)+fread(buffer,1,L,stdin); if(S==T) return EOF; } return *S++; } inline int Get_Int() { int re=0; char c; do c=Get_Char(); while(c<'0'||c>'9'); while(c>='0'&&c<='9') re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char(); return re; } } namespace Pairing_Heap{ struct Heap; struct Stack_Point{ Heap *mem; Stack_Point *last; void* operator new (size_t,Heap *_,Stack_Point *__); void operator delete (void *p); }; stack<void*>bin; void* Stack_Point :: operator new (size_t,Heap *_,Stack_Point *__) { if( bin.size() ) { Stack_Point *re=(Stack_Point*)bin.top();bin.pop(); re->mem=_;re->last=__; return re; } static Stack_Point *mempool,*C; if(C==mempool) { C=new Stack_Point[1<<15]; mempool=C+(1<<15); } C->mem=_; C->last=__; return C++; } void Stack_Point :: operator delete (void *p) { bin.push(p); } class Stack{ private: Stack_Point *top; int size; public: inline Heap* Top() { return top->mem; } inline void Pop() { delete top; top=top->last; --size; } inline bool Empty() { return size==0; } inline int Size() { return size; } inline void Push(Heap* x) { top=new (x,top) Stack_Point; ++size; } }; struct Heap{ Heap *fa; Stack son; ll f; }*root,heap[M]; inline Heap* Merge(Heap *x,Heap *y) { if( x->f > y->f ) swap(x,y); y->fa=x; x->son.Push(y); return x; } inline void Decrease_Key(Heap *x) { if(x==root) return ; x->fa=0x0; root=Merge(x,root); } inline void Pop() { static Stack stack1,stack2; Stack *s1=&stack1,*s2=&stack2; while( !root->son.Empty() ) { if(root->son.Top()->fa==root) { s1->Push( root->son.Top() ), root->son.Top()->fa=0x0; } root->son.Pop(); } while(s1->Size()>=2) { while(s1->Size()>=2) { Heap *temp1=s1->Top();s1->Pop(); Heap *temp2=s1->Top();s1->Pop(); s2->Push( Merge(temp1,temp2) ); } if( s1->Size() ) s2->Push( s1->Top() ),s1->Pop(); swap(s1,s2); } if( s1->Size() ) root=s1->Top(),s1->Pop(); } void Initialize() { int i; root=&heap[1]; for(i=2;i<=n;i++) { heap[i].f=4557430888798830399ll; heap[i].fa=root; heap[1].son.Push(&heap[i]); } } } void Add(int x,int y,int z) { table[++tot].to=y; table[tot].f=z; table[tot].next=head[x]; head[x]=tot; } ll Dijkstra() { int i,j; Pairing_Heap::Initialize(); for(j=1;j<=n;j++) { int x=(Pairing_Heap::root)-(Pairing_Heap::heap); Pairing_Heap::Pop(); if(x==n) return Pairing_Heap::heap[n].f; for(i=head[x];i;i=table[i].next) if(Pairing_Heap::heap[table[i].to].f>Pairing_Heap::heap[x].f+table[i].f) { Pairing_Heap::heap[table[i].to].f=Pairing_Heap::heap[x].f+table[i].f; Pairing_Heap::Decrease_Key(&Pairing_Heap::heap[table[i].to]); } } } int main() { //freopen("3040.in","r",stdin); //freopen("3040.out","w",stdout); int i,x,y,z; cin>>n>>m; cin>>T>>rxa>>rxc>>rya>>ryc>>rp; for(i=1;i<=T;i++) { X=(X*rxa+rxc)%rp; Y=(Y*rya+ryc)%rp; x=min(X%n+1,Y%n+1); y=Y%n+1; z=100000000-100*x; if(x!=y) Add(x,y,z); } for(i=T+1;i<=m;i++) { x=IStream::Get_Int(); y=IStream::Get_Int(); z=IStream::Get_Int(); Add(x,y,z); } cout<<Dijkstra()<<endl; }