思路:倒序处理,因为保证联通,我就先把操作离线下来,构造一颗末状态下的树(子集,末状态也可能是图),对于树,显然所有边都是关键路径。
若末状态是图,那剩下的边连在树上形成环,环上所有边都不是关键路径,等价于 u <-> v 路径边权赋值 0
操作同理
#include <bits/stdc++.h>
#define re register int
#define lp p << 1
#define rp p << 1 | 1
using namespace std;
const int N = 3e4 + 10, M = 1e5 + 10;
struct Edge
{
int to, w, next;
}e[M << 1];
int idx, h[N];
struct Tree
{
int l, r, sum, tag;
}t[N << 2];
int n, m, id[N], cnt, a[N], mat_a[N];
int fa[N], son[N], dep[N], siz[N], top[N];
struct Get_edge
{
int x, y;
}g[M];
bool vis[M];
struct Query
{
int c, x, y;
}q[M];
int num;
map<pair<int, int>, bool> mp;
vector<int> ans;
int f[N];
int find(int x)
{
return f[x] == x ? x : f[x] = find(f[x]);
}
inline void add(int x, int y, int w)
{
e[++ idx] = (Edge){y, w, h[x]};
h[x] = idx;
}
void dfs(int u, int fu)
{
fa[u] = fu;
dep[u] = dep[fu] + 1;
siz[u] = 1;
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to, w = e[i].w;
if (v == fu) continue;
a[v] = w;
dfs(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
}
}
void mark(int u, int t)
{
top[u] = t;
id[u] = ++ cnt;
mat_a[id[u]] = a[u];
if (son[u]) mark(son[u], t);
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to;
if (v == fa[u] || v == son[u]) continue;
mark(v, v);
}
}
inline void push_up(int p)
{
t[p].sum = t[lp].sum + t[rp].sum;
}
inline void push_down(int p)
{
if (t[p].tag != -1)
{
t[lp].sum = t[rp].sum = t[p].tag;
t[lp].tag = t[rp].tag = t[p].tag;
t[p].tag = -1;
}
}
inline void build(int p, int l, int r)
{
t[p].l = l, t[p].r = r;
t[p].tag = -1;
if (l == r)
{
t[p].sum = mat_a[l];
return;
}
int mid = (l + r) >> 1;
build(lp, l, mid);
build(rp, mid + 1, r);
push_up(p);
}
inline void update(int p, int l, int r, int k)
{
if (l <= t[p].l && t[p].r <= r)
{
t[p].sum = k;
t[p].tag = k;
return;
}
push_down(p);
int mid = (t[p].l + t[p].r) >> 1;
if (l <= mid) update(lp, l, r, k);
if (r > mid) update(rp, l, r, k);
push_up(p);
}
inline int query(int p, int l, int r)
{
if (l <= t[p].l && t[p].r <= r) return t[p].sum;
push_down(p);
int res = 0;
int mid = (t[p].l + t[p].r) >> 1;
if (l <= mid) res += query(lp, l, r);
if (r > mid) res += query(rp, l, r);
return res;
}
inline void update_path(int u, int v, int k)
{
if (u == v) return;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]]) swap(u, v);
update(1, id[top[u]], id[u], k);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
update(1, id[v] + 1, id[u], k);
}
inline int query_path(int u, int v)
{
if (u == v) return 0;
int res = 0;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]]) swap(u, v);
res += query(1, id[top[u]], id[u]);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
res += query(1, id[v] + 1, id[u]);
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
// freopen("lane9.in", "r", stdin);
// freopen("lane9.ans", "w", stdout);
cin >> n >> m;
for (re i = 1; i <= m; i ++) cin >> g[i].x >> g[i].y;
while (1)
{
int op; cin >> op;
if (op == -1) break;
else
{
int x, y; cin >> x >> y;
q[++ num] = (Query){op, x, y};
mp[{min(x, y), max(x, y)}] = true;
}
}
for (re i = 1; i <= n; i ++) f[i] = i;
for (re i = 1; i <= m; i ++)
{
int x = g[i].x, y = g[i].y;
if (!mp[{min(x, y), max(x, y)}] && find(x) != find(y))
{
f[find(x)] = find(y);
add(x, y, 1);
add(y, x, 1);
vis[i] = true;
// cout << x << " --- " << y << '\n';
}
}
dfs(1, 0); mark(1, 1);
build(1, 1, n);
for (re i = 1; i <= m; i ++) // 末状态图子集树构造好后,剩下没有连的边且不在操作集合中
{
int x = g[i].x, y = g[i].y;
if (!mp[{min(x, y), max(x, y)}] && !vis[i])
{
update_path(x, y, 0);
// cout << x << ' ' << y << '\n';
}
}
for (re i = num; i >= 1; i --)
{
int c = q[i].c, x = q[i].x, y = q[i].y;
if (c)
ans.push_back(query_path(x, y));
else
update_path(x, y, 0);
}
for (re i = ans.size() - 1; i >= 0; i --) cout << ans[i] << '\n';
return 0;
}