set由于其功能强大,令人爱不释手。
但是换来的是令人作呕的常数。
其实在一些简单的操作中,set是可以用priority_queue来替代的,后者常数小,代码相对来说也要简洁一些。
堆优化dij的priority_queue写法:
*SPFA已死,有事烧纸
正常的堆写法是需要删除的,显然set是可以做到这一点的,需要用pair,很烦。
问题1:
c++优先队列默认是大顶堆,我们需要取出的是距离最小的。
解决:
如果不写那些骚到不行的c++语法,最好用的方法就是结构体+重载了。
不用担心重载的常数问题,经过实测这个是几乎可以忽略滴。
问题2:
c++优先队列不支持删除,然而我们在更新最短距离后需要删除原来的。
解决:
这个采取懒惰删除法。
我们在取出堆顶后,判断它是否已经被确定了最短路,如果已经被确定了,说明它是懒惰删除没有删除的,pop()后继续取即可。
struct nod {
ll s; int x;
} a[N];
bool operator <(nod a, nod b) {return a.s > b.s;}
priority_queue<nod> q;
//...
while(!q.empty()) {
nod b = q.top(); q.pop(); int x = b.x;
if(bz[x]) continue; bz[x] = 1;
for(int i = fi[x]; i; i = nt[i]) if(b.s + v[i] < a[to[i]].s)
a[to[i]].s = b.s + v[i], q.push(a[to[i]]);
}
带删除的堆:
由于dij的特殊性,我们可以通过是否确定了最短路来判断这个堆顶应不应该删除。
但是对于所有堆这显然不行,一个有意思的trick是再开一个删除堆,每次两个堆的堆顶,如果相等,则同时pop()
大概是这么写:
struct head {
priority_queue<int> a, b;
void ins(int x) { a.push(x);}
void del(int x) { b.push(x);}
void pop() {
while(b.size() && a.top() == b.top())
a.pop(), b.pop();
a.pop();
}
int top() {
while(b.size() && a.top() == b.top())
a.pop(), b.pop();
return a.top();
}
int size() {return a.size() - b.size();}
int stop() { //second maximum
int x = top(); pop();
int y = top(); ins(x);
return y;
}
};