• 算法笔记--基环树


    基环树:无向图,一个环,环上每个点都是树根

     

    完备的扣环方法(可以扣二元环):

    void get_loop(int u) {
        vis[u] = ++vs;
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].to;
            if(v == fa[u]) continue;
            if(vis[v]) {
                if(vis[v] < vis[u]) continue;
                loop[++cnt] = v;
                for ( ;v != u; v = fa[v]) {
                    loop[++cnt] = fa[v];
                }
            }
            else fa[v] = u, get_loop(v);
        }
    }

    例1:BZOJ 1791

    思路:对于每个基环树,求出直径,然后加起来

    基环树求直径方法,将基环树的环扣出来,求出以环上每个点为根节点的树的直径以及深度,然后在环上求边权前缀和,枚举j, 那么答案就是sum[j] - sum[i] + deep[j] + deep[i],

    用单调队列维护max{deep[i] - sum[i]}就行了,扣环的时候还可以用基环内向树建图扣,不过就不好求树的直径了。

    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize(4)
    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define pi acos(-1.0)
    #define LL long long
    //#define mp make_pair
    #define pb push_back
    #define ls rt<<1, l, m
    #define rs rt<<1|1, m+1, r
    #define ULL unsigned LL
    #define pll pair<LL, LL>
    #define pii pair<int, int>
    #define piii pair<pii, int>
    #define mem(a, b) memset(a, b, sizeof(a))
    #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    #define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout);
    //head
    
    const int N = 1e6 + 5;
    deque<int> q;
    bool vs[N];
    pii loop[N], fa[N];
    int  cnt = 0, sz = 0, st, s, now, tot = 0, vis[N], head[N];
    LL deep[N*2], sum[N*2], mx = 0;
    struct edge {
        int to, w, nxt;
    }edge[N*2];
    void add_edge(int u, int v, int w) {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    void get_loop(int u) {
        vis[u] = ++sz;
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].to;
            if(v == fa[u].fi) continue;
            if(vis[v]) {
                if(vis[v] < vis[u]) continue;
                loop[++cnt].fi = v;
                loop[cnt].se = edge[i].w;
                for ( ;v != u; v = fa[v].fi) {
                    loop[++cnt] = fa[v];
                }
            }
            else fa[v].fi = u, fa[v].se = edge[i].w, get_loop(v);
        }
    }
    void dfs(int o, int u, LL x) {
        if(x >= mx) {
            mx = x;
            s = u;
        }
        for (int i = head[u]; ~i ; i = edge[i].nxt) {
            if(edge[i].to != o && (!vs[edge[i].to] || edge[i].to == now)) {
                dfs(u, edge[i].to, x+edge[i].w);
            }
        }
    }
    LL f(int id) {
        return deep[id] - sum[id];
    }
    int main() {
        mem(head, -1);
        int n, u, v;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d %d", &u, &v);
            add_edge(i, u, v);
            add_edge(u, i, v);
        }
        LL ans = 0;
        sum[0] = deep[0] = 0;
        for (int i = 1; i <= n; i++) {
            if(!vis[i]) {
                cnt = 0;
                sz = 0;
                LL t = 0;
                get_loop(i);
                for (int j = 1; j <= cnt; j++) vs[loop[j].fi] = true;
                for (int j = 1; j <= cnt; j++) {
                    mx = 0;
                    now = -1;
                    dfs(0, loop[j].fi, 0);
                    deep[j] = mx;
    
                    now = loop[j].fi;
                    dfs(0, s, 0);
                    t = max(t, mx);
                }
                for (int j = 1; j <= cnt; j++) deep[j+cnt] = deep[j];
                q.clear();
                for (int j = 1; j <= 2*cnt; j++) {
                    if(j <= cnt)sum[j] = sum[j-1] + loop[j].se;
                    else sum[j] = sum[j-1] + loop[j-cnt].se;
                    if(!q.empty()) t = max(t, f(q.front()) + sum[j] + deep[j]);
                    while(!q.empty() && f(q.back()) <= f(j)) q.pop_back();
                    q.push_back(j);
                    while(!q.empty() && q.front() <= j-cnt+1) q.pop_front();
                }
                ans += t;
            }
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    基环内向树:有向图,在基环树的基础上每个节点的出度为1

    基环外向树:有向图,在基环树的基础上每个节点的入度为1

     

  • 相关阅读:
    最小的K个数
    CentOS 7 连接不到网络解决方法
    数组中超过一半的数字
    字符串的排列
    二叉搜索树与双向链表
    复杂链表的复制
    二叉树中和为某一数值的路径
    二叉搜索树的后序遍历序列
    从上到下打印二叉树
    java的图形文档
  • 原文地址:https://www.cnblogs.com/widsom/p/9492725.html
Copyright © 2020-2023  润新知