HDU 3686 Tarjan v-DCC + 倍增 LCA

题意

传送门 HDU 3686

题解

求无向图中两条边之间的必经点。

v − D C C v-DCC vDCC 中任意两点都至少被包含在一个简单环中,则任意一条边都至少被包含在一个简单环中;则同一个 v − D C C v-DCC vDCC 中两条不相同的边之间至少存在两条不重叠的路径,两条边之间的必经点个数为边各自所在的 v − D C C v-DCC vDCC 之间的割点数量。

使用 T a r j a n Tarjan Tarjan 算法求解 v − D C C v-DCC vDCC,同时 D F S DFS DFS 求解原图的边所在的 v − D C C v-DCC vDCC 编号。 v − D C C v-DCC vDCC 缩点建立新图,由于图可能不连通,得到一颗树或森林。使用倍增 L C A LCA LCA 求解边 x , y x,y x,y 对应的 v − D C C v-DCC vDCC 节点间的割点数量即可。总时间复杂度 O ( M + N log ⁡ N ) O(M+N\log N) O(M+NlogN)

需要注意的是, v − D C C v-DCC vDCC 缩点建立新图时,需要将每个割点和 v − D C C v-DCC vDCC 作为新图中的节点,看似节点数没有增加;实际上若存在全是割点的 v − D C C v-DCC vDCC,会新增节点。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 10005, maxc = 2 * maxn, maxm = 2 * 100005, maxlg = 16;
int N, M, Q, num, dcc, dfn[maxn], low[maxn];
int top, st[maxn], e_dc[maxm], v_dc[maxn], n_id[maxn];
int tot, head[maxn], to[maxm], nxt[maxm];
int tot_c, hc[maxc], tc[2 * maxc], nc[2 * maxc];
int lg[maxc], fa[maxc][maxlg], dep[maxc], ds[maxc];
bool cut[maxn], vs[maxn], cut_c[maxc];
vector<int> Dcc[maxn];

void init()
{
    
    
    num = dcc = top = 0, tot = tot_c = 1;
    memset(dfn, 0, sizeof(dfn)), memset(dep, 0, sizeof(dep));
    memset(e_dc, 0, sizeof(e_dc)), memset(v_dc, 0, sizeof(v_dc));
    memset(head, 0, sizeof(head)), memset(hc, 0, sizeof(hc));
    memset(cut, 0, sizeof(cut)), memset(cut_c, 0, sizeof(cut_c));
    for (int i = 1; i <= N; ++i)
        Dcc[i].clear();
}

inline void add(int x, int y) {
    
     to[++tot] = y, nxt[tot] = head[x], head[x] = tot; }

inline void add_c(int x, int y) {
    
     tc[++tot_c] = y, nc[tot_c] = hc[x], hc[x] = tot_c; }

void label(int x)
{
    
    
    vs[x] = 1;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (v_dc[y] != dcc)
            continue;
        e_dc[i] = e_dc[i ^ 1] = dcc;
        if (!vs[y])
            label(y);
    }
}

void tarjan(int x, int rt)
{
    
    
    dfn[x] = low[x] = ++num;
    st[++top] = x;
    int fg = 0;
    if (x == rt && !head[x])
        ++dcc, Dcc[dcc].push_back(x);
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (!dfn[y])
        {
    
    
            tarjan(y, rt);
            low[x] = min(low[x], low[y]);
            if (low[y] >= dfn[x])
            {
    
    
                ++fg, ++dcc;
                if (x != rt || fg > 1)
                    cut[x] = 1;
                int z;
                do
                {
    
    
                    z = st[top--], Dcc[dcc].push_back(z), v_dc[z] = dcc;
                } while (z != y);
                Dcc[dcc].push_back(x), v_dc[x] = dcc;
                for (int j = 0; j < (int)Dcc[dcc].size(); ++j)
                    vs[Dcc[dcc][j]] = 0;
                label(x);
            }
        }
        else
            low[x] = min(low[x], dfn[y]);
    }
}

void dfs2(int x, int f, int d, int w)
{
    
    
    fa[x][0] = f, dep[x] = d, ds[x] = w;
    for (int k = 1; k <= lg[d]; ++k)
        fa[x][k] = fa[fa[x][k - 1]][k - 1];
    for (int i = hc[x]; i; i = nc[i])
    {
    
    
        int y = tc[i];
        if (y != f)
            dfs2(y, x, d + 1, w + cut_c[y]);
    }
}

int lca(int x, int y)
{
    
    
    if (dep[x] < dep[y])
        swap(x, y);
    while (dep[x] > dep[y])
        x = fa[x][lg[dep[x] - dep[y]]];
    if (x == y)
        return x;
    for (int k = lg[dep[x]]; k >= 0;)
        if (fa[x][k] != fa[y][k])
            x = fa[x][k], y = fa[y][k], k = lg[dep[x]];
        else
            --k;
    return fa[x][0];
}

int main()
{
    
    
    lg[0] = -1;
    for (int i = 1; i < maxc; ++i)
        lg[i] = lg[i - 1] + (i == (1 << (lg[i - 1] + 1)));
    while (~scanf("%d%d", &N, &M) && (N | M))
    {
    
    
        init();
        for (int i = 1, x, y; i <= M; ++i)
            scanf("%d%d", &x, &y), add(x, y), add(y, x);
        for (int i = 1; i <= N; ++i)
            if (!dfn[i])
                tarjan(i, i);
        int id = dcc;
        for (int i = 1; i <= N; ++i)
            if (cut[i])
                n_id[i] = ++id, cut_c[id] = 1;
        for (int i = 1; i <= dcc; ++i)
            for (int j = 0; j < (int)Dcc[i].size(); ++j)
            {
    
    
                int x = Dcc[i][j];
                if (cut[x])
                    add_c(i, n_id[x]), add_c(n_id[x], i);
            }
        for (int i = 1; i <= id; ++i)
            if (!dep[i])
                dfs2(i, 0, 0, 0);
        scanf("%d", &Q);
        for (int i = 1, x, y, z; i <= Q; ++i)
        {
    
    
            scanf("%d%d", &x, &y);
            x = e_dc[x << 1], y = e_dc[y << 1], z = lca(x, y);
            printf("%d\n", ds[x] + ds[y] - ds[z] - ds[fa[z][0]]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114989219