• 图论-怅望祁连(小知识点整理)


    各种小知识点

    Kruskal 重构树

    Steiner 树

    两道例题:

    BZOJ3205 APIO2013 机器人
    BZOJ2595 WC2008 游览计划

    (N) 个点的连通无向图中至少包含给定的 (K) 个点的最小生成树。

    (S) 表示连通状态, (p) 是当前状态中用来考虑转移的节点,可以理解成暂时的根之类的东西。

    [f(S,p)=min{f(A,p)+f(B,p) | A cup B = S}\f(S,p)=min{f(S,q)+val(p)} ]

    第一个转移直接 DP ,第二个转移要求 (p)(q) 关联,用图论算法转移。它的正确性好像来自什么三角形不等式,不懂,不过确实可以感性理解。

    注意 SPFA 可以加个 Small Label First 优化,开两个队列,每次取最小的来扩展,特殊情况下就是个 BFS 。

    给自己留一份伪代码:

    
    void spfa(int s)
    {
        sort(Que_A);
        ...
        while () {
            p = min(Que_A.head, Que_B.head);
            for (...) {
                if (F[s][y] > F[s][x] + w) {
                    ...
                    Que_B.push();
                }
            }
        }
        return;
    }
    
    void steiner()
    {
        for (int s = 0; s != (1 << K); ++s) {
            for (int t = (s - 1) & 1; t; t = (t - 1) & 1) {
                for (int p = 1; p <= N; ++p)
                    F[s][p] = min(F[s][p], F[t][p] + F[s ^ t][p]);
            }
            for (int p = 1; p <= N; ++p)
                if (F[s][p] < INF)
                    Que_A[++Q_cnt] = data(p, F[s][p]);
            spfa(s);
        }
        int ans = INF;
        for (int i = 1; i <= N; ++i)
            ans = min(ans, F[(1 << K) - 1][i]);
        return;
    }
    

    虚树

    树很大,需要考虑的点很少,弄一颗虚树。树上路径求交的方法是把点集按 dfn 排序,然后相邻两个点找 lca 记录路径,这些路径是无公共边的。虚数也是先排序,然后用栈维护 dfn 递增的一条从根到当前讨论的点的链,最后在栈底的就是虚数的根。

    void ins_node(int p)
    {
        if (Top <= 1) {
            S[++Top] = p;
            return;
        }
        int lca = get_lca(p, S[Top]);
        if (lca == S[top]) {
            S[++Top] = p;
            return;
        }
        while (Top > 1 && Dfn[lca] <= Dfn[S[Top - 1]])
            ins_edge(S[Top - 1], S[Top]), --Top;
        if (lca != S[Top])
            ins_edge(lca, S[Top]), S[top] = lca;
        S[++Top] = p;
        return;
    }
    

    仙人掌 & 圆方树

    小蒟蒻yyb的博客 - 仙人掌&圆方树学习笔记

    仙人掌定义:一条边属于至多一个简单环。
    桥边定义:非环边。
    环边定义:非桥边。

    首先,这个东西是处理无向图的。普通的圆方树只能处理仙人掌图,只对每个环建立方点,再向环上的点连边,其他点是圆点;广义的圆方树就是圆点和方点交替出现,对于桥边,强行在中间塞一个方点,然后还是对点双建立方点。

    普通圆方树,一般套路是桥边直接转移,环上单独 DP :

    void process_circle(int x, int y)
    {
        for (int i = Dad[y]; i != x; i = Dad[i])
     		...F[i]...
        ...F[x]...
        return;
    }
     
    void dfs(int p, int d)
    {
        Dad[p] = d;
        for (...) {
            if (!Dfn[t])
                ...
            else
                ...
            if (Low[t] > Dfn[p])
                F[p] = ...F[t]...
        }
        for (int i = Fst[p]; i; i = Nxt[i]) {
            if (Dad[t] != p && Dfn[t] > Dfn[p])
                process_circle(p, t);
        }
        return;
    }
    

    广义圆方树:

    void dfs(int p)
    {
        for (...) {// 遍历 p 能到达的点 t 
            if (!Dfn[t]) {
            	...
                if (Low[t] >= Dfn[p]) {
                    ins_edge(p, ++Tot);// p 到方点
                    int tmp;
                    do
                        tmp = S[S_top--], ins_edge(tmp, Tot);// 方点到环上其他点
                    while (tmp != t);// 注意 p 可能属于多个点双,所以弹栈到 t 为止
                }
            } else {
            	...
            }
        }
        return;
    }
    

    2-sat

    边都是单向的,表示一种同类关系限制,即选了 a 就一定要选 b ,或者说 a 和 b 有相同的取值$。

    注意不要让一些合法点的 Belong[i] == 0 ,全部都 dfs 一遍比较好。 Belong[i] == 0 会导致一些神奇的 Belong[i] == Belong[i'] 的情况,尽管此时实际上并没有产生矛盾。

    输出任意方案时,对于对称图,可以根据反边边拓扑排序,然后每次讨论 (i) 时,如果值为 0 就把 (i) 标记为 1 ,对应点 (i') 标记为 -1 ,如果值不为 0 就 continue 。可以证明,一组对应变量里选 SCC 编号较小者就是一组合理方案,考虑 Tarjan 搜索树 ++SCC 的操作即可。据说非对称图选 SCC 编号较小者也是正确的,而且此时前面拓扑排序的方法是错误的。所以好像选 SCC 编号较小者这种方法完全踩爆前一种方法?

  • 相关阅读:
    【HDOJ】1243 反恐训练营
    Eclipse 点击 Run 自动生成 out 文件的错误
    经纬度转凯立德 K 码
    Android开发环境建立
    Android 学习过程中遇到的知识点
    Android
    Android
    素数距离问题
    取石子(一)
    素数求和问题
  • 原文地址:https://www.cnblogs.com/ghcred/p/10284942.html
Copyright © 2020-2023  润新知