1. 程式人生 > 實用技巧 >圖論學習(在更)

圖論學習(在更)

note:

  • 最小環

  • 是列舉已有的最短路和k點連線的兩條邊, 保證三個點不重合。

 for(R int k = 1; k <= n; k ++) {
        for(R int i = 1; i < k; i ++)
            for(R int j = i + 1; j < k; j ++) {
              	if(e[i][j] < 1e9) 
                ans = min(ans, a[i][k] + a[k][j] + e[i][j]);
            }	
  • LuoguP2886

    矩陣乘法不代表\(\text{floyed, floyed}\)在於列舉一個新點,用其他點經過這個點的路徑來更新最短路。矩陣乘法中的乘法的\(k\)是列舉一箇中間點,使得兩個圖中的路徑可以連線。同時, 本題經過\(n\)條邊的最短路事實上是嚴格可以經過\(n\)條邊的,這也是為什麼這題看起來做了\(\log_n\)次但是卻看起來實現了個和\(\text{floyed}\)差不多的玩意。

  • \(\text{Dijkstra}\) 解決負權邊問題

    先建出\(0\)號點, 向所有點連一條邊權為\(0\)的邊,防止圖不連通,然後從\(0\)號點開始用\(\text{spfa}\)跑出所有點的最短路, 然後對於每一條邊\((u, v)\)

    , 加上權值\(dis[u] - dis[v]\), 由於\(dis[u] + w >= dis[v]\),所以每條邊的權值在修改後會大於0.按照這個方法直接跑\(\text{dijskra}\),最後的結果加上\(dis[end] - dis[start]\)即可。注意判負環的時候由於新加入了一個點,要到訪問次數\(>=n +1\)的時候退出。

  • 卡SPFA

  • 差分約束 : \(dis[a]+w[i]>=dis[b])\)

  • 帶權並查集

    在合併以前進行路徑壓縮,則\(x\)點直接指向\(rt_x\),\(y\)點直接指向\(rt_y\),若要把\(x\)合併到\(y\)

    上, 設\(k\)\(x\)\(y\)之間的距離, 有\(d_{x -> fx} + d_{fx -> fy} = k + d_{y -> fy}\),所以可以求出\(d_{fx->fy}\),直接更新即可。在路徑壓縮的時候, \(d[x] += d[tmp]\),\(tmp\)是路徑壓縮以前的父節點。所以事實上,一個點的\(d\)值, 描述的是這個點到這個點的父節點的距離而不是到根的距離。所以帶全並查集在查詢之前應先對點進行路徑壓縮。

    inline int find(int x) {
    	if(x != fa[x]) {
    		int tmp = fa[x];
    		fa[x] = find(fa[x]);
    		dep[x] = (dep[x] + dep[tmp] + 3) % 3;
    	}
    	return fa[x];
    }
    
  • 拓撲排序相關題目反過來建圖有奇怪的好處。

  • 長鏈剖分

    • 解決\(k\)級祖先時, 在所有長鏈的鏈頭存下,由於\(k\)級祖先所在長鏈的長度必定大於等於\(k\),取$2^r <= k $且\(2^{r + 1}> k\),當前點的\(2^r\)次祖先所在的長鏈長度必定大於\(2^r\), 所以可以在\(vector\)上直接確定\(k\)級祖先。預處理倍增即可。
    • 優化\(dp\)的時候利用指標繼承重兒子陣列, 然後聽說複雜度\(O(n)\),先把\(dp\)學好再搞。[POI2014]HOT-Hotels
  • 尤拉路 & 歐拉回路

    • 尤拉路直接從奇點\(dfs\)完事。

    • 歐拉回路從任意一點開搜, \(dfs\)的時候及時修改表頭, 防止複雜度退化, 同時標記已經訪問的邊, 在回溯的時候把點壓到棧中, 倒序輸出即可獲得點序列。魔改一下就是邊序列。

  • 雙連通分量

    • 關於點雙,兩個情況
      • \(x != rt\), 當\(\text{dfs}\)樹上點\(y\)的子樹無法指向\(x\)以上的點, 則\(x\)是割點, 即\(low[y] >= dfn[x]\)
      • \(x == rt\), 當\(x\)有兩棵以上子樹的時候,這是割點。
    • 關於邊雙,\(\text{dfs}\)的時候存一條邊防止訪問到父節點,當\(low[y] > dfn[x]\)則為割邊。
  • 仙人掌

    • 仙人掌圖的判定

      • 仙人掌首先是個強連通圖, 所以先跑\(\text{tarjan}\), 然後在\(\text{dfs}\)的時候可能會有返祖邊, 這個時候就是找到了一個環, 對這個環暴力打標記, 如果打了兩次以上立刻退出, 顯然複雜度是\(O(n + m)\)的。模板 (有點問題)

        • 咕咕
      • 圓方樹

      • 暴力對每個點雙建樹就完了。注意出棧的判定。

      • 一點性質就是可以用方點儲存整個點雙的資訊。

      • 可以考慮方點只維護兒子圓點的權值。

             while(1) {
         				int v = stk[tp];
         				add(v, tot); add(tot, v);
         				tp --;
         				if(v == y) break;
         			}
    
  • 2-SAT

    • 考慮拆點, 如果一個狀態選了, 另一個狀態必選, 就從這個狀態向另一個狀態連邊。

    • 如果兩個衝突點在一個強連通分量裡, 無解

    • 對於每一組拆開的點,選擇拓撲序較大的那個點, 也就是強連通分量標號較小的那個點, 因為選了拓撲序小的就一定要選所有和它相連的可能就會要求選大的, 但是選了大的一定不會對小的造成影響。也就是說這樣一定可以構造出一組合法的解。

    • 對於要求答案字典序的話可以使用\(dfs\),從而做到\(O(n(n +m))\).

  • 匹配

    • 匈牙利演算法

    • Hall定理

  • 網路流

    • 最大流

      • 注意\(bfs\)的時候重置\(cur\)陣列。
      • \(bfs\)的時候要噹噹前點未訪問過且當前邊有流量再擴充套件
      • \(dfs\)的時候及時更新表頭\(cur\)陣列
      • 初始的時候\(cnt\)設為1方便改反向邊流量
      • \(dfs\)的時候要求下一個點是下一層且走這條邊有流量才更新, 當前點限制滿了以後直接退出。
    • 最小費用最大流

      • 每次從\(S\)點開始跑\(spfa\)
      • 可以更新最短路且這條邊有流量的時候更新,更新的時候記錄前驅點, 這條邊, 更新\(y\)處的流限制。
      • 跑出路以後, 先給費用加上流乘距離, 然後從匯點開始倒推, 正向邊減,反向邊加即可。
      • 注意建邊的時候反向邊的權值為負
    • 無源匯有上下界可行流

      • 先把下界流滿, 然後建立虛擬源點向流入量大於流出量的點連邊權為這個差的邊, 流入量小於這個點的流出量的點向匯點連邊, 邊權為這個差的相反數, 然後跑最大流。此時跑最大流時邊權的限制就是上界減去下界。如果跑滿了,也就是和源點相連的點都滿流了, 答案就是最大流加上這條邊的下界。
    • 有源匯有上下界最大/小流

      • 從匯點向源點連容量巨大邊, 轉化為上者。
      • 求最大流就跑最大流, 最小流就跑反過來的最大流, 用下界和減去這個流, 求最大流的時候就是用下界和加上這個流。