树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)
?
題目鏈接
題意:
給定一棵樹,每條邊有黑白兩種顏色,初始都是白色,現在有三種操作:
1 u v:u到v路徑(最短)上的邊都取成相反的顏色
2 u v:u到v路徑上相鄰的邊都取成相反的顏色(相鄰即僅有一個節點在路徑上)
3 u v:查詢u到v路徑上有多少個黑色邊
思路:
對樹進行樹鏈剖分,分成重鏈和輕鏈,用兩棵線段樹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上翻轉一次,結合圖可能更能理解。還有就是線段樹的操作了。
?
另外:
u可能沒有son[u],默認為虛點0,那么在線段樹上需要加上一句話:if (l == r) return ;
#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;
}
?
轉載于:https://www.cnblogs.com/Running-Time/p/5669064.html
總結
以上是生活随笔為你收集整理的树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 个性签名爱情繁体字
- 下一篇: 改变mac中launchpad图标大小