P4320 道路相遇

时间:2021-08-17
本文章向大家介绍P4320 道路相遇,主要包括P4320 道路相遇使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

圆方树的定义

圆方树是用来解决仙人掌图的问题的,那什么是仙人掌图呢?

即不存在边同时属于多个环的无向连通图是一棵仙人掌。

点双连通分量的定义

要介绍圆方树,首先要介绍点双连通分量。

一个点双连通图的一个定义是:图中任意两不同点之间都有至少两条点不重复的路径。

一种简单的定义:不存在割点的图。

但这种定义对于两点一边的图时是没用的,它没有割点,但是并不能找到两条不相交的路径,因为只有一条路径。(也可以理解为那一条路径可以算两次,但的确没有相交,因为不经过其他点)。

在点双连通图内,一个点可能属于多个点双,但是一条边属于恰好一个点双。

更多关于有向图的强连通分量的知识,请看我的博客 \(\to\) 强连通分量

更多关于点双连通分量的知识,请看我的博客 \(\to\) 双连通分量

继续介绍圆方树

关于圆方树的建图,也比较简单,将一个点双连通分量内的所有边删去,再将一个点双连通分量中的每个点向一个新建的点连边,这个新建的点即是方点。

所以在圆方树中有 \(n+c\) 个点,其中 \(n\) 是原图点数,\(c\) 是原图点双连通分量的个数。

每个点双都可以形成一个菊花图,多个菊花图通过原图中的割点连接在一起(因为点双的分隔点是割点)。

显然,圆方树中每条边连接一个圆点和一个方点。

在下面这张图中,\([1,2,3,4,5]\) 是圆点,\([6,7]\) 是方点。

而如果圆方树连通,则有以下性质:

  • 方点之间不会存在连边。

  • 原图的割点就是圆方树中度数大于 \(1\) 的圆点。

  • 圆方数是一棵非常好的树,即点数等于边数加 \(1\)

  • 圆方树上任意一条路径上圆点方点间隔分布。

  • 如果圆点的 \(size\)\(1\),那么一个圆点子树的 \(size\) 和就是它下面的所有点的数量。

  • 对于一个点双中的两点,它们之间简单路径的并集,恰好完全等于这个点双,即同一个点双中的两不同点 \(u\)\(v\) 之间一定存在一条简单路径经过给定的在同一个点双内的另一点 \(w\)。也就是说,考虑两圆点在圆方树上的路径,与路径上经过的方点相邻的圆点的集合,就等于原图中两点简单路径上的点集。

如果原图中某个连通分量只有一个点,则需要具体情况具体分析,我们在后续讨论中不考虑孤立点。

注意一条边连接两个点的在这里不算点双。

广义圆方树

普通圆方树只能解决仙人掌图上的问题,而广义圆方树则可以将所有无向图转化为圆方树处理。

广义圆方树性质:圆点方点相间,不存在两个‘’相同形状‘’的点相连。

与圆方树不同的是,广义圆方树需要把一条边连接两个点也看成一个点双,原本两个圆点有一条边相连,现在在中间插入一个方点间隔开就好了。

可以参照这张图

关于本题:洛谷 P4320 道路相遇

题目大意

给定一无向图,现在 yzh 要从 \(u\) 点到处于 \(v\) 点的 cxr 家(我们也不知道他要去干什么),求所有从 \(u\)\(v\) 的路径中的必经点。

解题思路

介绍完上面的圆方树后,就会发现这题很简单,必经点个数其实就是两点之间割点的个数。

其实就是上面这条定理:

原图的割点就是圆方树中度数大于 \(1\) 的圆点。

得出,必经点数等于圆方树上两点路径上圆点数。

由于圆方树上任意一条路径上圆点方点间隔分布,所以需要除以 \(2\)

直接跑一边点双。

再建图。

然后跑一边树链剖分,记录深度和最近公共祖先。

对于每一组询问,直接用树上差分的知识求解就行了。

即设 \(lca\)\(u\)\(v\) 的最近公共祖先,则 \(Ans=(dep_u+dep_v-2*dep_{lca}) \div 2+1\)

AC CODE

阅读时请省略前面的快读。

#include <bits/stdc++.h>
using namespace std;
const int _ = 2e6 + 5;

struct Fastio
{
    template <typename T>
    Fastio operator>>(T &x)
    {
        x = 0;
        char c = getchar();
        while (c < '0' || c > '9')
            c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        return *this;
    }
    Fastio &operator<<(const char *str)
    {
        int cur = 0;
        while (str[cur])
            putchar(str[cur++]);
        return *this;
    }
    template <typename T>
    Fastio &operator<<(T x)
    {
        if (x == 0)
        {
            putchar('0');
            return *this;
        }
        if (x < 0)
            putchar('-'), x = -x;
        static int sta[45];
        int top = 0;
        while (x)
            sta[++top] = x % 10, x /= 10;
        while (top)
            putchar(sta[top] + '0'), --top;
        return *this;
    }

} io;

int n, m, q, tp;
int cnt_node, cntn;
int dfn[_], low[_];
int dep[_], top[_], siz[_], hson[_], fa[_];
stack<int> s;

struct Graph
{
    int tot, head[_], nxt[_ << 1], to[_ << 1];
    void add(int u, int v)
    {
        nxt[++tot] = head[u];
        to[tot] = v;
        head[u] = tot;
        nxt[++tot] = head[v];
        to[tot] = u;
        head[v] = tot;
    }
} G, T;

void tarjan(int u)
{
    dfn[u] = low[u] = ++cnt_node;
    s.push(u);
    for (int i = G.head[u], v; i; i = G.nxt[i])
    {
        v = G.to[i];
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u])
            {
                T.add(++cntn, u);
                while (1)
                {
                    int now = s.top();
                    s.pop();
                    T.add(cntn, now);
                    if (now == v)
                        break;
                }
            }
        }
        else
            low[u] = min(low[u], dfn[v]);
    }
}

void dfs1(int u, int d = 1)
{
    siz[u] = 1;
    dep[u] = d;
    for (int i = T.head[u], v; i; i = T.nxt[i])
    {
        v = T.to[i];
        if (dep[v])
            continue;
        fa[v] = u;
        dfs1(v, d + 1);
        siz[u] += siz[v];
        if (siz[v] > siz[hson[u]])
            hson[u] = v;
    }
}

void dfs2(int u, int topf = 1)
{
    top[u] = topf;
    if (!hson[u])
        return;
    dfs2(hson[u], topf);
    for (int i = T.head[u], v; i; i = T.nxt[i])
    {
        v = T.to[i];
        if (top[v])
            continue;
        dfs2(v, v);
    }
}

int LCA(int x, int y)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]])
            swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}

signed main()
{
    io >> n >> m;
    cntn = n;
    for (int i = 1, u, v; i <= m; i++)
    {
        io >> u >> v;
        G.add(u, v);
    }
    tarjan(1);
    dfs1(1);
    dfs2(1);
    io >> q;
    while (q--)
    {
        int u, v;
        io >> u >> v;
        int lca = LCA(u, v);
        io << (dep[u] + dep[v] - 2 * dep[lca]) / 2 + 1 << "\n";
    }
    return 0;
}

本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/15154205.html

原文地址:https://www.cnblogs.com/orzz/p/15154205.html