BZOJ 3040 最短路(road) Pairing-Heap优化Dijkstra

题目大意:给定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;
}


你可能感兴趣的:(dijkstra,bzoj,BZOJ3040,pairing-heap)