Solution
实际上就是查询 $u$ 到 $v$ 路径上 边双的个数 $ -1$。
并且题目仅有删边, 那么就离线倒序添边。
维护 边双 略有不同:
首先需要一个并查集, 记录 边双内的点。 在 添加边$(u,v)$时 , 若$u, v$ 已经相连, 那么把 $u, v$ 路径上的点 缩成一个点, 用最上面的点 来代替。
1 void del(int x, int y) { 2 if (!x) return; 3 fa[x] = y; 4 del(lc(x), y); 5 del(rc(x), y); 6 }
access 也应该变化 : $f[x]$ 可能已经经过压缩了。
1 void access(int x) {
2 for (int y = 0; x; y = x, x = f[y] = anc(f[x]))
3 splay(x), ch[x][1] = y, up(x);
4 }
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 using namespace std; 6 7 const int N = 3e4 + 5; 8 const int M = 1e5 + 5; 9 10 int n, m; 11 int fa[N], ans[N], vis[M]; 12 13 struct edge { 14 int u, v; 15 16 bool operator < (const edge &b) const { 17 return u == b.u ? v < b.v : u < b.u; 18 } 19 }e[M]; 20 21 struct que { 22 int op, u, v; 23 }q[M]; 24 25 int read() { 26 int X = 0, p = 1; char c = getchar(); 27 for (; c > '9' || c < '0'; c = getchar()) 28 if (c == '-') p = -1; 29 for (; c >= '0' && c <= '9'; c = getchar()) 30 X = X * 10 + c - '0'; 31 return X * p; 32 } 33 34 int anc(int x) { 35 return fa[x] == x? x : fa[x] = anc(fa[x]); 36 } 37 38 namespace LCT { 39 int f[N], sz[N], ch[N][2], tun[N]; 40 #define lc(x) ch[x][0] 41 #define rc(x) ch[x][1] 42 43 int isroot(int x) { 44 return rc(f[x]) != x && lc(f[x]) != x; 45 } 46 47 int get(int x) { 48 return rc(f[x]) == x; 49 } 50 51 void rev(int x) { 52 swap(lc(x), rc(x)); 53 tun[x] ^= 1; 54 } 55 56 void up(int x) { 57 sz[x] = 1; 58 if (lc(x)) sz[x] += sz[lc(x)]; 59 if (rc(x)) sz[x] += sz[rc(x)]; 60 } 61 62 void pushdown(int x) { 63 if (tun[x]) { 64 if (lc(x)) rev(lc(x)); 65 if (rc(x)) rev(rc(x)); 66 tun[x] = 0; 67 } 68 } 69 70 void pd(int x) { 71 if (!isroot(x)) 72 pd(f[x]); 73 pushdown(x); 74 } 75 76 void rotate(int x) { 77 int old = f[x], oldf = f[old], son = ch[x][get(x) ^ 1]; 78 if (!isroot(old)) ch[oldf][get(old)] = x; 79 ch[x][get(x) ^ 1] = old; 80 ch[old][get(x)] = son; 81 f[son] = old; f[x] = oldf; f[old] = x; 82 up(old); up(x); 83 } 84 85 void splay(int x) { 86 pd(x); 87 for (; !isroot(x); rotate(x)) 88 if (!isroot(f[x])) 89 rotate(get(f[x]) == get(x) ? f[x] : x); 90 } 91 92 void access(int x) { 93 for (int y = 0; x; y = x, x = f[y] = anc(f[x])) 94 splay(x), ch[x][1] = y, up(x); 95 } 96 97 int findr(int x) { 98 access(x); splay(x); 99 while (lc(x)) pushdown(x), x = lc(x); 100 return x; 101 } 102 103 void mroot(int x) { 104 access(x); splay(x); rev(x); 105 } 106 107 void split(int x, int y) { 108 mroot(x); access(y); splay(y); 109 } 110 111 void del(int x, int y) { 112 if (!x) return; 113 fa[x] = y; 114 del(lc(x), y); 115 del(rc(x), y); 116 } 117 118 void link(int x, int y) { 119 mroot(x); 120 f[x] = y; 121 } 122 123 void merge(int x, int y) { 124 x = anc(x); y = anc(y); 125 if (x == y) 126 return; 127 mroot(x); 128 if (findr(y) != x) 129 link(x, y); 130 else { 131 splay(x); 132 del(rc(x), x); 133 rc(x) = 0; up(x); 134 } 135 } 136 } 137 138 using namespace LCT; 139 140 int main() 141 { 142 n = rd; m = rd; 143 for (int i = 1; i <= n; ++i) 144 fa[i] = i; 145 for (int i = 1; i <= m; ++i) { 146 e[i].u = rd; e[i].v = rd; 147 if (e[i].u > e[i].v) 148 swap(e[i].u, e[i].v); 149 } 150 sort(e + 1, e + 1 + m); 151 int Q = 0; 152 for (; ; ) { 153 int op = rd; 154 if (op == -1) 155 break; 156 int u = rd, v = rd; 157 if (u > v) 158 swap(u, v); 159 q[++Q].op = op; q[Q].u = u; 160 q[Q].v = v; 161 edge t; 162 t.u = u; t.v = v; 163 if (op == 0) 164 vis[lower_bound(e + 1, e + 1 + m, t) - e] = 1; 165 } 166 for (int i = 1; i <= m; ++i) 167 if (!vis[i]) 168 merge(e[i].u, e[i].v); 169 for (int i = Q; i; i--) { 170 if (q[i].op == 1) { 171 int x = q[i].u, y = q[i].v; 172 x = anc(x); y = anc(y); 173 if (x == y) {ans[i] = 0; continue;} 174 split(x, y); 175 ans[i] = sz[y] - 1; 176 } 177 else merge(q[i].u, q[i].v); 178 } 179 for (int i = 1; i <= Q; ++i) 180 if (q[i].op == 1) 181 printf("%d ", ans[i]); 182 }