P4320:道路相遇题解

做法

我们发现,其实这道题就是要求询问无向图上点对之间的割点数量:可以确定割点的数量是唯一的,因为如果有更多的割点,那么不存在更少的割点使点双连通分量连通。

既然我们知道了大概的题意和要求,那么我们可以发现:如果把这些割点所在域内的非割点全部缩成一个点,然后让割点分别连接,那么根据「不存在更少的割点使点双连通分量连通」的「唯一性」,可以知道这样建出来的东西一定是一棵树。

这样就很好做了!直接树上差分+LCA即可。我们需要用 Tarjan 把这些点缩起来,通过割点进行连接,然后做一遍 DFS 之类的获取前缀和,再处理 LCA 倍增数组,我们就可以在线回答询问了。算法的复杂度为\(\Theta(n \log n + m \log n)\)。

代码

只有 80 分,有三个点被卡了。

// P4320.cpp
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 2e6 + 3;

int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], dep[MAX_N];
int ncnt, ptot, tail, cut[MAX_N], aff[MAX_N], dist[MAX_N], org_dist[MAX_N], fa[24][MAX_N];
vector<int> DCCs[MAX_N], G[MAX_N];

struct edge
{
    int to, nxt;
} edges[MAX_N << 1];

inline char getChar()
{
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) +
                             fread(buf, 1, 100000, stdin),
                        p1 == p2)
               ? EOF
               : *p1++;
}

int read()
{
    int x = 0, f = 1;
    char ch = getChar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getChar();
    }
    while (isdigit(ch))
        x = (x << 3) + (x << 1) + ch - '0', ch = getChar();
    return x * f;
}

void addpath(int src, int dst)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    head[src] = current++;
}

void tarjan(int u, int org)
{
    dfn[u] = low[u] = ++ptot, stk[++tail] = u;
    int cnt = 0;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (dfn[edges[i].to] == 0)
        {
            tarjan(edges[i].to, org), low[u] = min(low[u], low[edges[i].to]);
            if (low[edges[i].to] >= dfn[u])
            {
                // maybe a cut;
                cnt++;
                if (cnt >= 2 || u != org)
                    cut[u] = 1;
                ncnt++;
                do
                {
                    DCCs[ncnt].push_back(stk[tail]), aff[stk[tail]] = ncnt;
                } while (stk[tail--] != edges[i].to);
                // can't pop the cut out of the stack;
                // there must be 2 components connected by the cut;
                DCCs[ncnt].push_back(u), aff[u] = ncnt;
            }
        }
        else
            low[u] = min(low[u], dfn[edges[i].to]);
}

void bfs(int src)
{
    queue<int> q;
    q.push(src), dep[src] = 1;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = 0, siz = G[u].size(); i < siz; i++)
            if (dep[G[u][i]] == 0)
            {
                dep[G[u][i]] = dep[u] + 1, dist[G[u][i]] += dist[u];
                fa[0][G[u][i]] = u, q.push(G[u][i]);
            }
    }
}

int getLCA(int x, int y)
{
    if (dep[x] < dep[y])
        swap(x, y);
    for (int i = 23; i >= 0; i--)
        if (dep[fa[i][x]] >= dep[y])
            x = fa[i][x];
    if (x == y)
        return x;
    for (int i = 23; i >= 0; i--)
        if (fa[i][x] != fa[i][y])
            x = fa[i][x], y = fa[i][y];
    return fa[0][x];
}

int query(int x, int y)
{
    int lca = getLCA(x, y);
    return dist[x] + dist[y] - dist[lca] - dist[fa[0][lca]] - org_dist[x] - org_dist[y] + 2;
}

int main()
{
    memset(head, -1, sizeof(head));
    n = read(), m = read();
    for (int i = 1, u, v; i <= m; i++)
        u = read(), v = read(), addpath(u, v), addpath(v, u);
    // get the DCCs and make it into a fucking tree;
    for (int i = 1; i <= n; i++)
        if (dfn[i] == 0)
            tarjan(i, i);
    int tree_tot = ncnt;
    for (int i = 1; i <= n; i++)
        if (cut[i])
            aff[i] = ++tree_tot, dist[aff[i]] = org_dist[aff[i]] = 1;
    for (int cp = 1; cp <= ncnt; cp++)
        for (int i = 0, siz = DCCs[cp].size(); i < siz; i++)
            if (cut[DCCs[cp][i]])
                G[cp].push_back(aff[DCCs[cp][i]]), G[aff[DCCs[cp][i]]].push_back(cp);
    for (int i = 1; i <= tree_tot; i++)
        if (dep[i] == 0)
            bfs(i);
    for (int i = 1; i <= 23; i++)
        for (int j = 1; j <= tree_tot; j++)
            fa[i][j] = fa[i - 1][fa[i - 1][j]];
    q = read();
    while (q--)
    {
        int x, y;
        x = read(), y = read();
        printf("%d\n", query(aff[x], aff[y]));
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *