F. Rada and the Chamomile Valley
F. Rada and the Chamomile Valley
Yesterday, Rada found a portal that can transport her to the Chamomile Valley and back. Rada's happiness knew no bounds, but it didn't last long — she suddenly realized that she didn't know where and when any of the Smeshariki would be.
The Chamomile Valley consists of $n$ houses and $m$ lanes connecting the houses. The lanes are numbered from $1$ to $m$. You can walk along the lanes in both directions. It is known that from any house, you can reach any other house via the lanes, and there is no lane connecting a house to itself. Moreover, any two houses are connected by at most one lane.
Rada knows that the Smeshariki walk every day from house number $1$ to house number $n$, but she doesn't know which specific lanes they will take. Rada will be in the Chamomile Valley on each of the next $q$ days. On the $k$-th day, she will be at house number $c_k$.
Since Rada does not know which specific lanes the Smeshariki will take, she is only interested in those lanes that they will definitely use. To ensure she does not miss any of them, she wants to know the index of the nearest such lane on each day. Rada is too busy strolling through the Chamomile Valley, so she asks you to help her determine the required lane indices.
The distance from house $c$ to the lane connecting houses $a$ and $b$ is defined as the minimum of $\rho(a, c)$ and $\rho(b, c)$, where $\rho(a, b)$ is the minimum number of lanes needed to reach house number $b$ starting from house number $a$.
Input
The first line of the input contains an integer $t$ ($1 \leq t \leq 10^4$) — the number of test cases. The description of each test case follows.
The first line contains two integers $n$ and $m$ ($1 \leq n \leq 2 \cdot 10^5$, $n-1 \leq m \leq \min(\frac{n \cdot (n-1)}{2}, 2 \cdot 10^5)$) — the number of houses and lanes, respectively.
The next $m$ lines contain two integers $u \neq v$ ($1 \leq u, v \leq n$) — a lane connecting houses numbered $u$ and $v$. The lanes are given in order of numbering, that is, the description of the first lane comes first, followed by the second, third, and so on up to the $m$-th lane.
Next, an integer $q$ ($1 \leq q \leq 2 \cdot 10^5$) is given — the number of days Rada will be walking in the Chamomile Valley.
The next $q$ lines each contain a single integer $c$ ($1 \leq c \leq n$) — the house at which Rada will be on that day.
It is guaranteed that from any house, you can reach any other house by only using the lanes, and there are no lanes from a house to itself, and any two houses are connected by at most one lane.
It is guaranteed that the sum of $n$ across all test cases does not exceed $2 \cdot 10^5$, the sum of $m$ across all test cases does not exceed $2 \cdot 10^5$, and the sum of $q$ across all test cases does not exceed $2 \cdot 10^5$.
Output
For each test case, output the answer for each of the days. If there are multiple suitable lanes on any of the days, output the lane with the smallest index among the suitable ones. If there are no required lanes, output $-1$.
Examples
Input
3
3 3
1 2
2 3
3 1
1
1
5 4
1 2
2 3
3 4
4 5
3
1
2
3
7 6
1 2
1 5
2 3
3 4
5 7
6 7
7
1
2
3
4
5
6
7Output
-1 
1 1 2 
2 2 2 2 2 5 5 Input
2
5 4
4 2
4 1
5 1
3 5
5
1
2
3
4
5
6 6
1 2
2 3
3 4
4 5
5 2
5 6
2
3
4Output
3 3 3 3 3 
1 6 Note
In all further explanations, we denote the transition from house $a$ to house $b$ via the lane numbered $c$ as $a \xrightarrow{c} b$.
In the first sample, from house $1$ to house $3$, you can reach it via at least the following paths:
$$1 \xrightarrow{3} 3$$
$$1 \xrightarrow{1} 2 \xrightarrow{2} 3$$
As we can see, these two paths do not share any common lanes, which means there are no suitable lanes.
In the second sample, it can be noted that there is a unique path from $1$ to $n$:
$$1 \xrightarrow{1} 2 \xrightarrow{2} 3 \xrightarrow{3} 4 \xrightarrow{4} 5$$
As can be seen, the answer for the $v$-th vertex is $\max(v-1,\ 1)$.
解题思路
如果某条边是 $1$ 到 $n$ 必然经过的边(即任意一条 $1$ 到 $n$ 的路径都包含这条边),那么删除该边后,$1$ 和 $n$ 将不再处于同一个连通块中。另外有桥的定义(摘自 OI Wiki):
对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。严谨来说,就是:假设有连通图 $G=\{V,E\}$, $e$ 是其中一条边(即 $e \in E$),如果 $G-e$ 是不连通的,则边 $e$ 是图 $G$ 的一条割边(桥)。
因此 $1$ 到 $n$ 必然经过的边就是桥。为此我们可以先找出原图的所有桥,可以通过 Tarjan 算法求边双连通分量进行缩点,那么原图就会变成一棵无向树,树中连接着每个节点(连通分量)的边就是原图中的桥。接着还需要在这些桥中找到只有从 $1$ 到 $n$ 才会经过的桥,只需以 $1$ 所在的连通分量为起点,对缩点后得到的树进行 dfs,找到到达 $n$ 所在的连通分量时经过的所有边(桥),将这些边构成的集合记为 $E$,$E$ 中每条边的两个端点构成的集合记为 $V$。那么 $E$ 就是 $1$ 到 $n$ 必然会经过的所有边。
对于每个询问节点 $u$,我们需要以 $u$ 为起点在原图中跑一遍 bfs,计算 $u$ 到 $V$ 中每个点的最短距离,从而确定离 $u$ 最近的边的编号,显然这很容易卡到 $O\left((n+m)^2\right)$。考虑到每个查询都关注节点到集合 $V$ 中节点的最短距离,我们可以转换思路:以 $V$ 中的所有节点作为源点进行多源 bfs,这样得到的结果等价于原图中任意节点到 $V$ 中节点的最短距离。在 bfs 的过程中再额外维护距离节点最近的边的编号($E$ 中的边), 就可以通过一次 bfs 求得任意一个点的答案,从而在询问时做到 $O(1)$ 查询。
不过上述过程的代码实现起来还蛮复杂的,下面大概讲一下具体细节。
- 首先建图,为了方便将每条边的编号作为边的权重。
- 然后对原图跑 Tarjan 算法,确定每个节点所在的连通分量的编号,每个连通分量的编号对应缩点后无向树的节点编号。
- 接着建立无向树,只需遍历原图中的所有边,如果该边的两个端点不在同一个连通分量,则说明该边是桥,同时对这两个连通分量进行连边。
- 然后以 $1$ 所在的连通分量对应的节点为根,对无向树进行 dfs,并在这个过程中维护一个数组用来记录访问到当前节点所经过的边的编号(对应边权)。当访问到 $n$ 时,此时数组就记录了所有 $1$ 到 $n$ 必然经过的边。
- 然后枚举所有被记录的边,将边的两个端点压入到 bfs 的队列中。记得节点避免重复入队,如果原图是菊花图的话,bfs 的时候会被卡到 $O(n^2)$。然后就是多源 bfs,在维护最短距离的同时,再维护到达节点的编号最小的边。
AC 代码如下,时间复杂度为 $O(n+m+q)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = N * 4;
int n, m;
int h1[N], h2[N], e[M], wt[M], ne[M], idx;
int dfn[N], low[N], tot;
int stk[N], tp;
int id[N], cnt;
bool vis[N], st[N];
int q[N], d[N], p[N];
void add(int *h, int u, int v, int w) {
    e[idx] = v, wt[idx] = w, ne[idx] = h[u], h[u] = idx++;
}
void tarjan(int u, int p) {
    dfn[u] = low[u] = ++tot;
    stk[++tp] = u;
    for (int i = h1[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (!dfn[v]) {
            tarjan(v, i);
            low[u] = min(low[u], low[v]);
        }
        else if (i != (p ^ 1)) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (dfn[u] == low[u]) {
        cnt++;
        int t;
        do {
            t = stk[tp--];
            id[t] = cnt;
        } while (t != u);
    }
}
bool dfs(int u, int p) {
    if (u == id[n]) return true;
    for (int i = h2[u]; i != -1; i = ne[i]) {
        int v = e[i], w = wt[i];
        if (v == p) continue;
        vis[w] = true;
        if (dfs(v, u)) return true;
        vis[w] = false;
    }
    return false;
}
void solve() {
    cin >> n >> m;
    idx = 0;
    memset(h1, -1, n + 1 << 2);
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        add(h1, u, v, i);
        add(h1, v, u, i);
    }
    tot = cnt = 0;
    memset(dfn, 0, n + 1 << 2);
    tarjan(1, -1); // 缩点
    memset(h2, -1, cnt + 1 << 2);
    for (int i = 1; i <= n; i++) { // 枚举原图所有边
        for (int j = h1[i]; j != -1; j = ne[j]) {
            int u = id[i], v = id[e[j]], w = wt[j];
            if (u != v) add(h2, u, v, w); // 找到桥并在缩点后的图连边
        }
    }
    dfs(id[1], 0); // 找到从1到n路径上的所有桥
    int hh = 0, tt = -1;
    memset(d, 0x3f, n + 1 << 2);
    memset(p, 0x3f, n + 1 << 2);
    memset(st, 0, n + 1);
    for (int i = 1; i <= n; i++) { // 枚举原图所有边
        for (int j = h1[i]; j != -1; j = ne[j]) {
            int u = i, v = e[j], w = wt[j];
            if (vis[w]) { // 该边是1到n路径上的桥
                auto add = [&](int u) {
                    p[u] = min(p[u], w);
                    if (st[u]) return; // 避免节点重复入队
                    st[u] = true;
                    q[++tt] = u;
                    d[u] = 0;
                };
                add(u);
                add(v);
                vis[w] = false; // 避免无向边被重复枚举
            }
        }
    }
    while (hh <= tt) {
        int u = q[hh++];
        for (int i = h1[u]; i != -1; i = ne[i]) {
            int v = e[i];
            if (d[v] > d[u] + 1) {
                d[v] = d[u] + 1;
                p[v] = p[u];
                q[++tt] = v;
            }
            else if (d[v] == d[u] + 1) {
                p[v] = min(p[v], p[u]);
            }
        }
    }
    cin >> m;
    while (m--) {
        int x;
        cin >> x;
        cout << (p[x] == 0x3f3f3f3f ? -1 : p[x]) << ' ';
    }
    cout << '\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}
参考资料
Codeforces Round 1043 (Div. 3) Editorial:https://codeforces.com/blog/entry/145692
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/19053662
 
                    
                
 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号