題目連接php
題意:c++
給定一棵樹,每條邊有黑白兩種顏色,初始都是白色,如今有三種操做:ui
1 u v:u到v路徑(最短)上的邊都取成相反的顏色3d
2 u v:u到v路徑上相鄰的邊都取成相反的顏色(相鄰即僅有一個節點在路徑上)blog
3 u v:查詢u到v路徑上有多少個黑色邊ip
思路:get
對樹進行樹鏈剖分,分紅重鏈和輕鏈,用兩棵線段樹W,L來維護。W維護樹上在重鏈上的u和v之間的邊的翻轉狀況(操做在線段樹上的[pos[v],pos[u]]區間);L維護樹上在重鏈上的u和v之間的相鄰邊的翻轉狀況
。那麼某一個點u與它父親節點fa[u]的邊的最終翻轉狀況爲:W(pos[u], pos[u])(若是邊是重鏈上的邊),W(pos[u], pos[u])^L(pos[fa[u]], pos[fa[u]])(若是邊是輕鏈)。對於1操做,只要簡單的在W上維護就能夠了。對於2操做,除了在L上操做,還要注意頭和尾的特殊處理(由於對於重鏈內的點,不包括頭尾,只在W上查詢),也就是u的重鏈上的兒子son[u]以及u的鏈頭p=belong[u]要在W上翻轉一次,結合圖可能更能理解。還有就是線段樹的操做了。it
另外:class
u可能沒有son[u],默認爲虛點0,那麼在線段樹上須要加上一句話:if (l == r) return ;im
#include <bits/stdc++.h> const int N = 1e5 + 5; //線段樹 #define lson l, mid, o << 1 #define rson mid + 1, r, o << 1 | 1 struct Seg_Tree { int fp[N<<2], s[N<<2]; void flip(int l, int r, int o) { s[o] = (r - l + 1) - s[o]; fp[o] ^= 1; } void push_up(int o) { s[o] = s[o<<1] + s[o<<1|1]; } void push_down(int l, int r, int o) { if (fp[o]) { int mid = l + r >> 1; flip (lson); flip (rson); fp[o] = 0; } } void build(int l, int r, int o) { fp[o] = s[o] = 0; if (l == r) { return ; } int mid = l + r >> 1; build (lson); build (rson); } void updata(int ql, int qr, int l, int r, int o) { if (ql <= l && r <= qr) { flip (l, r, o); return ; } if (l == r) return ; //! push_down (l, r, o); int mid = l + r >> 1; if (ql <= mid) updata (ql, qr, lson); if (qr > mid) updata (ql, qr, rson); push_up (o); } int query(int ql, int qr, int l, int r, int o) { if (ql <= l && r <= qr) { return s[o]; } push_down (l, r, o); int mid = l + r >> 1, ret = 0; if (ql <= mid) ret += query (ql, qr, lson); if (qr > mid) ret += query (ql, qr, rson); push_up (o); return ret; } }W, L; std::vector<int> edge[N]; int sz[N], dep[N], son[N], fa[N]; int pos[N], belong[N]; int loc; int n; int query(int u, int v) { int p = belong[u], q = belong[v], ret = 0; while (p != q) { if (dep[p] < dep[q]) { std::swap (p, q); std::swap (u, v); } if (u != p) { ret += W.query (pos[son[p]], pos[u], 1, n, 1); } ret += (W.query (pos[p], pos[p], 1, n, 1) ^ L.query (pos[fa[p]], pos[fa[p]], 1, n, 1)); u = fa[p]; p = belong[u]; } if (u == v) return ret; if (dep[u] < dep[v]) { std::swap (u, v); } ret += W.query (pos[son[v]], pos[u], 1, n, 1); return ret; } void modify(int t, int u, int v) { int p = belong[u], q = belong[v]; while (p != q) { if (dep[p] < dep[q]) { std::swap (p, q); std::swap (u, v); } if (t == 1) { W.updata (pos[p], pos[u], 1, n, 1); } else { L.updata (pos[p], pos[u], 1, n, 1); W.updata (pos[son[u]], pos[son[u]], 1, n, 1); W.updata (pos[p], pos[p], 1, n, 1); } u = fa[p]; p = belong[u]; } if (dep[u] < dep[v]) { std::swap (u, v); } if (t == 1) { if (u == v) return ; W.updata (pos[son[v]], pos[u], 1, n, 1); } else { L.updata (pos[v], pos[u], 1, n, 1); W.updata (pos[son[u]], pos[son[u]], 1, n, 1); W.updata (pos[v], pos[v], 1, n, 1); } } //樹鏈剖分 void DFS2(int u, int chain) { pos[u] = ++loc; belong[u] = chain; if (son[u]) { DFS2 (son[u], chain); } for (auto v: edge[u]) { if (v == fa[u] || v == son[u]) continue; DFS2 (v, v); } } void DFS1(int u, int pa) { sz[u] = 1; dep[u] = dep[pa] + 1; son[u] = 0; fa[u] = pa; for (auto v: edge[u]) { if (v == pa) continue; DFS1 (v, u); sz[u] += sz[v]; if (sz[son[u]] < sz[v]) son[u] = v; } } void prepare() { sz[0] = dep[0] = fa[0] = 0; DFS1 (1, 0); loc = 0; DFS2 (1, 1); W.build (1, n, 1); L.build (1, n, 1); } void init_edge(int n) { for (int i=1; i<=n; ++i) { edge[i].clear (); } } int main() { int T; scanf ("%d", &T); while (T--) { scanf ("%d", &n); init_edge (n); for (int i=1; i<n; ++i) { int u, v; scanf ("%d%d", &u, &v); edge[u].push_back (v); edge[v].push_back (u); } prepare (); int q; scanf ("%d", &q); while (q--) { int t, u, v; scanf ("%d%d%d", &t, &u, &v); if (t == 3) { printf ("%d\n", query (u, v)); } else { modify (t, u, v); } } } return 0; }