zoukankan      html  css  js  c++  java
  • 点、边双,圆方树

    集训第一天,好累,果然停OI一个月还是不习惯

    讨论圆方树之前,我们先来考虑如下定义:

    点双:无向图的极大子图,使得该子图内无割点

    边双,无向图的极大子图,使得该子图内无割边

    易发现,一条边至多属于一个边双,一个点却可能不只属于一个点双

    求割边:考虑到当边(u,v)满足dfn[u] < dfn[v]且low[v] > dfn[u],此时割掉(u,v),v及其子树为一个边双

    易得将所有割边删去,原图即为若干边双

    求割点类似,不过多讨论一种为根的情况即可

    当发现 low[v] >= dfn[u]时,把v及v以上的点从栈中弹出,再加上u, 共同形成⼀个点双。
    void tarjan(int u)
    {
        dfn[u] = low[u] = ++idx;
        st[++top] = u;
        for(int i = fir[u];i;i = e[i].next)
        {
            int v = e[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                low[u] = min(low[u],low[v]);
                if(low[v] >= dfn[u])
                {
                    ++cnt,a[n + cnt] = 1;    
                    tradde(u,n + cnt);
                    int t = 0;
                    while(t != v)
                    {
                        t = st[top--];
                        tradde(t,n + cnt);
                        a[n + cnt]++;
                    }
                }
            }
            else low[u] = min(low[u],dfn[v]);
        }
    }
    View Code

    圆方树

    原图中的每个点为圆点。

    现在将点双内部的边全部拆去,建立一个方点存储该点双内需要的信息,并将该方点与各个圆点相连

    这样处理后,整个图就变成一颗树,且易发现,只有圆点和方点之间有边

    因此就可以利用这些性质,做一些看似没有思路的题

    CF487E :tourists : https://www.luogu.org/problemnew/show/CF487E

    给出⼀张图,点有点权。每次询问两点之间的简单路径中,权值的
    最⼩值最⼩是多少。带修。n, m, q ≤ 1000000
     
    考虑建圆方树,每个方点存储该点双中权值最小值,树剖即可
     
    [APIO2018]铁人两项
    给出⼀个(不⼀定连通)的图,求有多少个三元组 (s, c, f) 满⾜ s, c, f 都是图中的点,且存在⼀条从s到c的路径和⼀条从c到f的路径,使得两条路径没有公共点(除c外)。三元组有序
     
    考虑O(n^2)暴力,建圆方树,枚举圆点点对,这时计算他们路径上有多少个点即可
    考虑到圆点会被计算两次,因此将方点的权值设为点双大小,圆点的权值设为-1,计算路径长度即可
     
    考虑优化,我们可以尝试计算每个点在路径中被包含了多少次,即两种:
    1.作为中转点:枚举每个子树v,则对答案贡献为sz[v] * (sum - (u <= n) - sz[v]) * a[u],这里a[u]为u的权值
    2.作为起点或终点,此时该点必须为圆点,答案为(总节点数-1) * 2(起终点各为一遍)
    另外再考虑u的子树以外的点的路径,则对答案贡献(sz[u]  - (u <= n)) * (sum - sz[u])
    注意:sz只计算圆点数,因此要特别考虑(可以综合上面式子理解)
    #define O(x) cout << #x << " " << x << endl; 
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (ans *= 10) += ch - '0';
             ch = getchar();
        }
        return ans * op;
    }
    const int maxn = 4e5 + 5;
    
    struct egde
    {
        int to,next;
    }e[maxn],tr[maxn];
    int head[maxn],tot;
    void tradde(int u,int v)
    {
        tr[++tot].next = head[u];
        head[u] = tot;
        tr[tot].to = v;
        swap(u,v);
        tr[++tot].next = head[u];
        head[u] = tot;
        tr[tot].to = v;    
    }
    int fir[maxn],alloc;    
    void adde(int u,int v)
    {
        e[++alloc].next = fir[u];
        fir[u] = alloc;
        e[alloc].to = v;
        swap(u,v);
        e[++alloc].next = fir[u];
        fir[u] = alloc;
        e[alloc].to = v;    
    }
    int n,m;
    int low[maxn],st[maxn],dfn[maxn],a[maxn],idx,top,cnt;
    int sum;
    ll ans;
    int sz[maxn];
    void tarjan(int u)
    {
        dfn[u] = low[u] = ++idx;
        st[++top] = u;
        for(int i = fir[u];i;i = e[i].next)
        {
            int v = e[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                low[u] = min(low[u],low[v]);
                if(low[v] >= dfn[u])
                {
                    ++cnt,a[n + cnt] = 1;    
                    tradde(u,n + cnt);
                    int t = 0;
                    while(t != v)
                    {
                        t = st[top--];
                        tradde(t,n + cnt);
                        a[n + cnt]++;
                    }
                }
            }
            else low[u] = min(low[u],dfn[v]);
        }
    }
    void dfs1(int u,int fa)
    {
        if(u <= n) sz[u] = 1;
        for(int i = head[u];i;i = tr[i].next)
        {
            int v = tr[i].to;
            if(v == fa) continue;
            dfs1(v,u);
            sz[u] += sz[v];
        }
    }        
    void dfs(int u,int fa)
    {
        ans = ans + 1ll * a[u] * (sz[u] - (a[u] == -1)) * (sum - sz[u]);
        for(int i = head[u];i;i = tr[i].next)
        {
            int v = tr[i].to;
            if(v == fa) continue;
            dfs(v,u);
            ans += 1ll * a[u] * sz[v] * (sum - (a[u] == -1) - sz[v]);
        }
        if(a[u] == -1) ans += 2ll * (sum - 1) * a[u];
    }
    int main()
    {
        memset(a,-1,sizeof(a));
        n = read(),m = read();
        for(int i = 1;i <= m;i++) 
        {
            int u = read(),v = read();
            adde(u,v);
        }
        for(int i = 1;i <= n;i++) 
        {
            if(!dfn[i]) 
            {
                sum = 0;
                tarjan(i);
                dfs1(i,0);
                sum = sz[i];
                dfs(i,0);
            }
        }
        printf("%lld",ans);
    }
        
        
    
        
    View Code

    总结:

    据说圆方树还可以解决仙人掌问题,但是菜鸡也不知道啥是仙人掌...

    感觉圆方树还有很多奇妙性质没有理解透彻,以后再巩固

  • 相关阅读:
    界面控件DevExpress WPF入门 表达式编辑器功能
    Telerik UI for WPF全新版本——拥有Office2019高对比度主题
    DevExpress报表控件v21.2 全新的Visual Studio报表设计器
    报告生成器FastReport .NET入门指南 在Linux中启动应用程序
    文档控件DevExpress Office File API v21.2 自定义字体加载引擎
    UI组件库Kendo UI for Angular入门 如何开始使用图表功能
    WPF界面工具Telerik UI for WPF入门级教程 设置一个主题(二)
    DevExtreme初级入门教程(React篇) TypeScript支持
    报表开发利器FastReport .NET v2022.1 添加关键对象和属性
    python项目打包(一) setup.py、Python源代码项目结构
  • 原文地址:https://www.cnblogs.com/LM-LBG/p/11219097.html
Copyright © 2011-2022 走看看