【數據結構】 配對堆&BZOJ 3040

【引子】

(BZOJ3040)
有一個m邊有向圖,節點從1~n編號,求從1到n的最短路。
n106,m107n \leq 10^6,m \leq 10^7


顯然是一個dijkstra+堆優化的板題。
然而一般的堆會被卡空間。
所以我們需要一個更高效的堆。
網上找了半天,可選的數據結構好像只有斐波那契堆和配對堆。然而前者好難寫啊
ε=(´ο`*)))唉。
接下來介紹一下配對堆。
(附:如果想偷懶不學這個東西,可使用平板電視考試時候爆了別怪我qwq,或者可以學學這位大佬***水過這題)

【配對堆】

配對堆和普通的堆不大一樣,它是一顆多叉樹,但依舊滿足堆頂的元素是一個最值。
對於一個配對堆,有以下幾個操作(以小根堆爲例):

·q.link()

該操作實現將兩個堆合併爲一個堆(A<B)。
在這裏插入圖片描述
只需將A和B直接連接即可。

在這裏插入圖片描述

	int link(int x, int y)
    {
        if(val[x] > val[y]) swap(x, y);
        int p = edges.pop();//獲取一個空位置,這麼做的目的是爲了儘量減少空間消耗
        to[p] = y, nxt[p] = fir[x], fir[x] = p, fa[y] = x; return x;//用鏈式前向星維護堆的樹結構
    }
·q.push()

將新加入的節點看做一個堆,然後同q.link()

void push(int x, ll y)
    {
        int p = nodes.pop(); //同edges
        pos[p] = x, cur[x] = p, val[p] = y, //記錄位置,方便之後的q.update()
        rt = rt ? link(p, rt) : p, ++siz;
    }
·q.update()

該操作將修改一個節點的值(注意爲了保持堆的結構,只能改爲更小的值)。
發現若將一個點入堆很多次,空間開銷將大大增加。所以我們需要這個操作來避免重複入堆。
首先將將要修改的點從堆中拆出來,修改之後,再執行q.link()即可。
在這裏插入圖片描述

void update(int x, ll y)
    {
        int p = cur[x];
        if(p && p != rt)
            fa[p] = 0;
        val[p] = y;
        if(rt != p)
            rt = link(rt, p);
    }
·q.top()

直接讀取根的信息即可,代碼略。

·q.pop()

上面的操作不僅很快(都是O(1)O(1)的),而且還很草,都是隨便亂連一通了事
爲了保證這個數據結構的時間複雜度足夠優秀,這個操作需要好好設計一下。其實十分簡單。
將根的兒子都拆下來,然後兩兩合併即可(合併的次序結構很像二叉樹由下而上)。
不想畫圖直接搬這個大佬的圖片
在這裏插入圖片描述
據說均攤時間複雜度是O(logn)O(logn),但是死活找不到證明。
但它快到飛起就不管這個了吧。


不難發現單次操作的時間複雜度爲O(logn)O(logn),而且空間也完全可以接受。

【代碼】

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mn = 1000005, mm = 10000005;
struct edge{
    int to, w;
}e[mm];
struct stk{
    int a[mn * 20], top, cnt;
    void push(int x) {a[++top] = x;}
    int pop() {return top ? a[top--] : ++cnt;}
};
int fir[mn], nxt[mm], cnt;
ll dis[mn]; bool vis[mn];
struct pair_heap{
    stk nodes, edges;
    int que[mn * 20]; ll val[mn];
    int to[mn], fir[mn], nxt[mn], fa[mn], pos[mn], cur[mn], rt, siz;
    int link(int x, int y)
    {
        if(val[x] > val[y]) swap(x, y);
        int p = edges.pop();
        to[p] = y, nxt[p] = fir[x], fir[x] = p, fa[y] = x; return x;
    }
    void push(int x, ll y)
    {
        int p = nodes.pop();
        pos[p] = x, cur[x] = p, val[p] = y, rt = rt ? link(p, rt) : p, ++siz;
    }
    void update(int x, ll y)
    {
        int p = cur[x];
        if(p && p != rt)
            fa[p] = 0;
        val[p] = y;
        if(rt != p)
            rt = link(rt, p);
    }
    int top() {return pos[rt];}
    void pop()
    {
        int h = 0, t = 0;
        for(int i = fir[rt]; i; i = nxt[i])
        {
            edges.push(i);
            if(fa[to[i]] == rt) que[++t] = to[i], fa[to[i]] = 0;
        }
        nodes.push(rt), cur[pos[rt]] = 0, pos[rt] = fir[rt] = fa[rt] = 0, rt = 0, --siz;
        while(h < t)
        {
            ++h;
            if(h == t) {rt = que[h]; return;}
            int a = que[h], b = que[++h]; que[++t] = link(a, b);
        }
    }
    bool empty() {return siz == 0;}
}q;
inline int getint()
{
    int ret = 0, flg = 1; char c;
    while((c = getchar()) < '0' || c > '9')
        if(c == '-') flg = -1;
    while(c >= '0' && c <= '9')
        ret = ret * 10 + c - '0', c = getchar();
    return ret * flg;
}
inline void addedge(int a, int b, int c) {e[++cnt] = (edge) {b, c}, nxt[cnt] = fir[a], fir[a] = cnt;}
inline void dijkstra(int s)
{
    memset(dis, 0x3f, sizeof dis), dis[s] = 0, q.push(s, 0);
    while(!q.empty())
    {
        int s = q.top();
        q.pop();
        if(vis[s]) continue;
        vis[s] = 1;
        for(int i = fir[s]; i; i = nxt[i])
        {
            int t = e[i].to;
            if(dis[t] > dis[s] + e[i].w)
            {
                if(dis[t] == 0x3f3f3f3f3f3f3f3fll) q.push(t, dis[s] + e[i].w);
                else q.update(t, dis[s] + e[i].w);
                dis[t] = dis[s] + e[i].w;
            }
        }
    }
}
int main()
{
    int n = getint(), m = getint(), s = getint();, T = getint(), rxa = getint(), rxc = getint(), rya = getint(), ryc = getint(), rp = getint();
    int x = 0, y = 0, c = 0, a, b;
    /*for(int i = 1; i <= T; i++)
    {
        x = (1ll * x * rxa + rxc) % rp,
        y = (1ll * y * rya + ryc) % rp,
        a = min(x % n + 1, y % n + 1),
        b = max(y % n + 1, y % n + 1), addedge(a, b, 1e8-100*a);
    }*/ //數據超水把隨機邊扔掉都能過(誰叫它邊權太大了呢ε=(´ο`*)))唉),
    	//不註釋直接TLE(大常數選手的無奈)
    for(int i = 1; i <= m; i++)
        a = getint(), b = getint(), c = getint(), addedge(a, b, c);
    dijkstra(s);
    for(int i = 1; i <= n; i++)
        printf("%lld ", dis[i]);
    puts("");
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章