P2542 [AHOI2005] 航线规划

P2542 [AHOI2005] 航线规划

题面

对 Samuel 星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了 Samuel 星球所在的星系——一个巨大的由千百万星球构成的 Samuel 星系。

星际空间站的 Samuel II 巨型计算机经过长期探测,已经锁定了 Samuel 星系中 (n) 个星球的空间坐标,并对这些星球以 (1)(n) 依次编号。

一些先遣飞船已经出发,在星球之间开辟探险航线。

探险航线是双向的,例如从 (1) 号星球到 (3) 号星球开辟探险航线,那么从 (3) 号星球到 (1) 号星球也可以使用这条航线。

例如下图所示:

avatar

(5) 个星球之间,有 (5) 条探险航线。

(A,B) 两星球之间,如果某条航线不存在,就无法从 (A) 星球抵达 (B) 星球,我们则称这条航线为关键航线。

显然上图中,(1) 号与 (5) 号星球之间的关键航线有 (1) 条:即为 (4leftrightarrow5) 航线。

然而,在宇宙中一些未知的磁暴和行星的冲撞,使得已有的某些航线被破坏,随着越来越多的航线被破坏,探险飞船又不能及时恢复这些航线,可见两个星球之间的关键航线会越来越多。

假设在上图中,航线 $4leftrightarrow24(从 (4) 号星球到 (2) 号星球)被破坏。此时,(1) 号与 (5) 号星球之间的关键航线就有 (3) 条:(1 leftrightarrow 3)(3 leftrightarrow 4)(4 leftrightarrow 5)

小联的任务是,不断关注航线被破坏的情况,并随时给出两个星球之间的关键航线数目。现在请你帮助完成。

输入格式

第一行有两个整数,分别表示星球个数 (n) 和初始时的航线条数 (m)

接下来 (m) 行,每行有两个不相同的整数 (u,v),表示星球 (u) 和星球 (v) 之间存在一条航线。

接下来有若干行,每行首先给出一个整数 (op),表示一次操作的类型。

(op=1),则后接两个整数 (u,v),表示询问当前 (u,v) 两星球之间有多少关键航线。
(op=0),则后接两个整数 (u,v),表示 (u,v) 之间的航线被破坏。
(op=−1),则表示输入结束,后面不再存在操作。

输出格式

对每个 (op=1) 的询问,输出一行一个整数表示关键航线数目。

输入输出样例

输入

5 5
1 2
1 3
3 4
4 5
4 2
1 1 5
0 4 2
1 5 1
-1

输出

1
3

说明/提示

数据规模与约定
对于全部的测试点,保证:
(1 leq n leq 3 imes 10^4,1 leq m leq 10^5)

$ -1 leq op leq 1,1 leq u, v leq n$

无论航线如何被破坏,任意时刻任意两个星球都能够相互到达。在整个数据中,任意两个星球之间最多只可能存在一条直接的航线。

对于 (op=0) 的操作,保证操作前航线 (u leftrightarrow v) 存在。

询问与破坏航线的总次数不超过 (4 imes 10^4)

题解

树上LCA + 并查集 + 树状数组

时间逆序, 变成了加边,

来到例题

只不过变成了两点之间的桥

还是dfs序的好性质, 记录dfs序和离开当前点的dfs序low, 则 x 的孩子dfs 都大于 dfn[x] 小于等于 low[x]

通过树状数组巧妙维护树形结构 x 到 树根 的距离, 则每次询问就是 ask(x) + ask(y) - 2 * ask(lca(x,y))

关键是加边的时候 怎么维护 x 到根的距离,

跟例题一样, 在并查集爬树的时候, add(dfn[x], 1), add(low[x] + 1, -1) 即可

struct STFrom {
    int f[N][20], dep[N], lg[N], t;//N为节点的数量
    vector<int>* h;
    void init(int n, vector<int>* H) {
        t = log2(n - 1) + 1; h = H; lg[0] = -1;
        rep(i, 1, n) dep[i] = 0, lg[i] = lg[i >> 1] + 1;
    }
    void bfs(int s) {
        queue<int> q; q.push(s); dep[s] = 1;
        rep(i, 0, t) f[s][i] = 0;
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (auto& y : h[x]) {
                if (dep[y]) continue;
                dep[y] = dep[x] + 1; f[y][0] = x; q.push(y);
                for (int j = 1; j <= t; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
            }
        }
    }
    int lca(int x, int y) {
        if (dep[x] > dep[y]) swap(x, y);
        for (int k = dep[y] - dep[x]; ~lg[k]; k ^= 1 << lg[k]) y = f[y][lg[k]];
        if (x == y) return x;
        per(i, lg[dep[y]], 0) if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    int dist(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)] << 1); }
} ST;

struct node { int x, y, op; };

int n, m, _, k, cas;
int f[N], c[N], dfn[N], low[N], df;
PII ed[M];
vector<int> h[N], ans;
vector<node> e;
set<PII> es;

int ff(int x) { return x == f[x] ? x : f[x] = ff(f[x]); }

void add(int x, int k) { for (; x <= n; x += -x & x) c[x] += k; }

int ask(int x) { int ans = 0; for (; x; x -= -x & x) ans += c[x]; return ans; }

void dfs(int x, int fa) {
    dfn[x] = ++df;
    for (auto& y : h[x]) if (y != fa) dfs(y, x);
    low[x] = df;
}

int main() {
    IOS; cin >> n >> m; rep(i, 1, n) f[i] = i;
    rep(i, 1, m) { cin >> ed[i].fi >> ed[i].se; if (ed[i].fi > ed[i].se) swap(ed[i].fi, ed[i].se); }
    stack<node> st;
    for (int op, x, y; cin >> op, op != -1;) {
        cin >> x >> y; if (x > y) swap(x, y);
        if (op == 1) st.push({ x, y, (int)ans.size() }), ans.pb(0);
        else st.push({ x, y, -1 }), es.insert({ x, y });
    }
    rep(i, 1, m) if (!es.count(ed[i]))
        if (ff(ed[i].fi) == ff(ed[i].se)) e.push_back({ ed[i].fi, ed[i].se, -1 });
        else h[ed[i].fi].pb(ed[i].se), h[ed[i].se].pb(ed[i].fi), f[f[ed[i].fi]] = f[ed[i].se];
    ST.init(n, h); ST.bfs(1); dfs(1, 0);
    rep(i, 1, n) f[i] = i;// add(dfn[i], ST.dep[i] - 1);
    while (!e.empty()) st.push(e.back()), e.pop_back();
    while (!st.empty()) {
        auto& [x, y, op] = st.top(); st.pop(); int z = ST.lca(x, y);
        int fx = ff(x), fy = ff(y), fz = ff(z);
        if (op < 0) {
            while (fx ^ fz) add(dfn[fx], -1), add(low[fx] + 1, 1), f[fx] = ff(ST.f[fx][0]), fx = f[fx];
            while (fy ^ fz) add(dfn[fy], -1), add(low[fy] + 1, 1), f[fy] = ff(ST.f[fy][0]), fy = f[fy];
        }
        else {
            ans[op] = ST.dep[x] + ST.dep[y] - 2 * ST.dep[z];
            ans[op] += ask(dfn[x]) + ask(dfn[y]) - 2 * ask(dfn[z]);
        }
    }
    for (auto& i : ans) cout << i << '
';
    return 0;
}
原文地址:https://www.cnblogs.com/2aptx4869/p/14508127.html