zoukankan      html  css  js  c++  java
  • 啊,这可恶的圆方树



    开新坑了——圆方树

    圆方树


    先来讲讲圆方树是个啥。
    相对于tarjan对于有向有环图进行的缩点,圆方树在tarjan的基础上进行了进一步的拓展.
    他能在无向图上进行,而对于有向图的强联通分量,则变为了无向图中的边双或点双。
    而功能也是更加的强大,无向图->树。
    但是也是对应能解决一部分的问题。
    比如圆方树的板子题:求任意两点之间一定要经过的点的个数。
    一定要经过的点,即为割点。
    对于一个无向图,一个没有割点部分是点双(但是这并不严谨,点双的定义为,点双所涵盖的图中,任意两点之间有两条以上的简单路径相连(个人的理解))

    点(边)双:若一个无向图中的去掉任意一个节点(一条边)都不会改变此图的连通性,即不存在割点(桥),则称作点(边)双连通图;(来自度娘)

    其实对于上面的圆方树板子题,一定要经过的点就是割点,因为除了这个点,我们是不能再找到另一个路径前往下一个边双的,不然他就不是割点了(删除它以后,对图的连通性没有影响)。
    其实圆方树原本是来解决仙人掌的问题的。因为对于仙人掌的每一个环,都是一个点双。。。

    所以圆方树如何实现呢?

    看这样的图

    对于第一个无向图,可以发现它有4个点双。
    在第二个图中,有4个方点,每一个对应了一个点双。
    如图,我们把每一个点双中的点连接到在新图中对应的方点上。
    这就变成了一个树。

    一个方点有且只有一个点双对应。但是一个点可能连接很多个方点,那么就说明这是个割点。
    那么怎么来求点双呢?
    tarjan在无向图中的应用之一就是求割点。
    其实也可以不用圆方树,可是圆方树好就好在建图极为的方便,和tarjan几乎是绝配,因为方点其实就是染色,所以在tarjan中就可以直接进行建树了,和有向图缩点的方向其实差不多


    建树具体方案如下:
    1. 根据tarjan算法,求出每个点双。
    2. 拆掉每个点双内部的所有边。
    3. 将这个点双对应的方点向这个点双内的每一个圆点连一条边。
    这样每一个点双就成为了一个树的形态,这样所有的都连起来也就是一颗树了。

    建树 代码

    #include<bits/stdc++.h>
    const int MN = 100005; int N, M, cnt; vector<int> G[MN], T[MN * 2]; //G是原图,T是圆方树 int dfn[MN], low[MN], dfc; int stk[MN], tp; void Tarjan(int u) { printf(" Enter : #%d ", u); low[u] = dfn[u] = ++dfc; // low 初始化为当前节点 dfn stk[++tp] = u; // 加入栈中 for (int i=0;i<G[u].size();i++) { // 遍历 u 的相邻节点        int v=G[u][i]; if (!dfn[v]) { // 如果未访问过 Tarjan(v); // 递归 low[u] = std::min(low[u], low[v]); // 未访问的和 low 取 min if (low[v] >= dfn[u]) { // 标志着找到一个以 u 为根的点双连通分量 ++cnt; // 增加方点个数 printf(" Found a New BCC #%d. ", cnt - N); // 将点双中除了 u 的点退栈,并在圆方树中连边 for (int x = 0; x != v; --tp) { //!!!! x = stk[tp]; T[cnt].push_back(x); T[x].push_back(cnt); printf("   BCC #%d has vertex #%d ", cnt - N, x) } // 注意 u 自身也要连边(但不退栈) T[cnt].push_back(u); T[u].push_back(cnt); printf("   BCC #%d has vertex #%d ", cnt - N, u); } } else low[u] = std::min(low[u], dfn[v]); // 已访问的和 dfn 取 min } printf(" Exit : #%d : low = %d ", u, low[u]); printf(" Stack:   "); for (int i = 1; i <= tp; ++i) printf("%d, ", stk[i]); puts(""); } int main() { scanf("%d%d", &N, &M); cnt = N; // 点双 / 方点标号从 N 开始 for (int i = 1; i <= M; ++i) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); // 加双向边 G[v].push_back(u); } // 处理非连通图 for (int u = 1; u <= N; ++u) if (!dfn[u]) Tarjan(u), --tp; // 注意到退出 Tarjan 时栈中还有一个元素即根,将其退栈 return 0;
    }

     这就是部分代码了。

    对于我说的那个末班题嘛,你还需要一个lca,可供选择的有:倍增,树链,balalbala,反正我jio得还是倍增好写 虽然慢

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    struct yuanx
    {
        ll next,to;
    }tree[2000001];
    struct edge
    {
        ll next,to,v;
    }e[2000001];
    ll head[2000001],head2[2000001],tot2,tot,n,m,Q,dep[2000001];
    ll f[2000001][23],fa[2000001],dfn[2000010],low[2000001],ccs;
    ll dfsc,cnt,xx1[2000001],yy1[2000001];
    bool vis[2000001],vis2[2000001];
    inline ll read()
    {
        char c=getchar();ll a=0,b=1;
        for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
        for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;
        return a*b;
    }
    void add(ll i,ll j)
    {
        e[++tot].next=head[i];
        e[tot].to=j;
        head[i]=tot;
    }
    void add2(ll i,ll j)
    {
        tree[++tot2].next=head2[i];
        tree[tot2].to=j;
        head2[i]=tot2;
    }
    void dfs(ll x,ll faa)
    {
        dep[x]=dep[faa]+1;
        for(ll i=1;(1<<i)<=dep[x];i++)
        {
            f[x][i]=f[f[x][i-1]][i-1];
        }
        for(ll i=head2[x];i!=0;i=tree[i].next)
        {
            ll u=tree[i].to;
            if(u==faa)continue;
            f[u][0]=x;
            dfs(u,x);
        }
    }
    ll lca(ll x,ll y)
    {
        if(dep[x]<dep[y])swap(x,y);
        for(ll i=22;i>=0;i--)
        {
            if(dep[f[x][i]]>=dep[y])
            {
                x=f[x][i];
            }
            if(x==y)
            {
                return x;
                
            }
        }
        if(x==y)
        {
            return x;
        }
        for(ll i=20;i>=0;i--)
        {
            if(f[x][i]!=f[y][i])
            {
                x=f[x][i];y=f[y][i];
            }
        }
        return f[x][0];
    }
    int s[2000001],top;
    void tarjan(ll x)
    {
        dfn[x]=low[x]=++dfsc;
        s[++top]=x;vis[x]=true;
        for(ll i=head[x];i!=0;i=e[i].next)
        {
            ll u=e[i].to;
            if(dfn[u]==0)
            {
                tarjan(u);
                low[x]=min(low[x],low[u]);
                if(low[u]>=dfn[x])
                {
                    cnt++;
                    for(int v=0;v!=u;top--)
                    {
                        v=s[top];
                        add2(v,cnt);
                        add2(cnt,v);
                    }
                    add2(cnt,x);add2(x,cnt);
                }
            }
            else
            if(vis[u]==true)
            {
                low[x]=min(low[x],dfn[u]);
            }
        }
    } 
    ll lcaa(ll x,ll y)
    {
        return (dep[x]+dep[y]-dep[lca(x,y)]*2-2)/2;
    }
    int main()
    { 
        n=read();m=read();
        cnt=n;
        for(ll i=1;i<=m;i++)
        {
            ll x=read();ll y=read();
            add(x,y);add(y,x);
            xx1[i]=x;yy1[i]=y;
        }
        for(ll i=1;i<=n;i++)
        {
            if(!dfn[i])
            {
                tarjan(i);
                top=0;
            }
        }
        dfs(1,1);
        Q=read();
        for(ll i=1;i<=Q;i++)
        {
            ll x=read();ll y=read();
            ll ans=0;
            ans=max(ans,lcaa(xx1[x],xx1[y]));
            ans=max(ans,lcaa(xx1[x],yy1[y]));
            ans=max(ans,lcaa(yy1[x],xx1[y]));
            ans=max(ans,lcaa(yy1[x],yy1[y]));
            cout<<ans<<endl;
        }
        return 0;
    }

    代码巨丑无比(逃

  • 相关阅读:
    9、实战快速上手
    8、路由【前端实现页面的跳转】
    7、Webpack的学习【打包工具】
    6、vue的安装【nodejs、vue-cli】
    5、计算属性、内容分发、自定义事件
    4、Axios异步通信
    3、Vue表单的双向绑定以及第一个Vue组件
    2、Vue的基本属性
    PHP算法之寻找两个有序数组的中位数
    PHP算法之无重复字符的最长子串
  • 原文地址:https://www.cnblogs.com/HLZZPawa/p/12826766.html
Copyright © 2011-2022 走看看