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\) 条:即为 \(4\leftrightarrow5\) 航线。

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

假设在上图中,航线 $4\leftrightarrow24(从 \(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 \times 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 \times 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 << '\n';
    return 0;
}
posted @ 2021-03-09 21:07  洛绫璃  阅读(117)  评论(0编辑  收藏  举报