【线段树】【LCT】【LCA】树点涂色(luogu 3703)
生活随笔
收集整理的這篇文章主要介紹了
【线段树】【LCT】【LCA】树点涂色(luogu 3703)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
樹點涂色
luogu 3703
題目大意
給出一棵樹,每個節點的初始顏色不同,做若干操作:
1.在一個點到根節點路徑上染上一種新的顏色
2.查詢一條路徑上有多少種不同的顏色
3.查詢一個點x,使該點到根節點路徑的不同顏色種數最多
輸入樣例
5 6 1 2 2 3 3 4 3 5 2 4 5 3 3 1 4 2 4 5 1 5 2 4 5輸出樣例
3 4 2 2數據范圍
1?n,m?1051\leqslant n,m\leqslant 10^51?n,m?105
解題思路
對于該圖,可以建立LCT,虛邊連接的是不同顏色的點,實邊連接的是相同顏色的點,然后對此進行操作
操作1和access操作相似
對于修改一個點x的顏色
對于修改鏈中的點,本來x貢獻為1,修改后為0,所以修改鏈深度大于y的p-1
而對于x原來所在鏈上的點,本來x貢獻為0,修改后為1,所以該鏈上深度大于y的p+1
操作2相當于x點到lca的不同顏色種數加上y點到lca的不同顏色種數
就是px+py?2×plca+1p_x+p_y-2\times p_{lca}+1px?+py??2×plca?+1
操作3可以按dfs序建立線段樹,這樣就可以查詢子樹內的最小值了
代碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define N 100010 using namespace std; int n, m, x, y, z, w, tot, s[N<<2], fa[N], bg[N], ed[N], dep[N], lazy[N<<2], head[N], son[N][2], faa[N][20]; struct rec {int to, next; }a[N<<1]; void add(int x, int y) {a[++tot].to = y;a[tot].next = head[x];head[x] = tot;return; } void downtate(int x)//線段樹 {if (!lazy[x]) return;s[x * 2] += lazy[x], lazy[x * 2] += lazy[x];s[x * 2 + 1] += lazy[x], lazy[x * 2 + 1] += lazy[x];lazy[x] = 0;return; } void up(int x) {s[x] = max(s[x * 2], s[x * 2 + 1]);return; } void change(int x, int L, int R, int l, int r, int y) {if (L == l && R == r){s[x] += y;lazy[x] += y;return;}downtate(x);int mid = (L + R) >> 1;if (r <= mid) change(x * 2, L, mid, l, r, y);else if (l > mid) change(x * 2 + 1, mid + 1, R, l, r, y);else change(x * 2, L, mid, l, mid, y), change(x * 2 + 1, mid + 1, R, mid + 1, r, y);up(x);return; } int ask(int x, int L, int R, int l, int r) {if (L == l && R == r) return s[x];int mid = (L + R) >> 1;downtate(x);if (r <= mid) return ask(x * 2, L, mid, l, r);else if (l > mid) return ask(x * 2 + 1, mid + 1, R, l, r);else return max(ask(x * 2, L, mid, l, mid), ask(x * 2 + 1, mid + 1, R, mid + 1, r)); } bool NR(int x)//LCT {return son[fa[x]][0] == x || son[fa[x]][1] == x; } bool IRS(int x) {return son[fa[x]][1] == x; } void rotate(int x) {int y = fa[x], z = fa[y], k = IRS(x), g = son[x][!k];if (NR(y)) son[z][IRS(y)] = x;if (g) fa[g] = y;son[x][!k] = y;son[y][k] = g;fa[x] = z;fa[y] = x;return; } void Splay(int x) {while(NR(x)){if (NR(fa[x])) rotate(IRS(x) == IRS(fa[x]) ? fa[x] : x);rotate(x);}return; } int find_root(int x) {while(son[x][0]) x = son[x][0];return x; } void access(int x) {for (int y = 0; x; x = fa[y = x]){Splay(x);int z = son[x][1];if (z) z = find_root(z), change(1, 1, n, bg[z], ed[z], 1);//對原鏈上的點貢獻+1if (y) z = find_root(y), change(1, 1, n, bg[z], ed[z], -1);//對修改的臉上的點貢獻-1son[x][1] = y;}return; } void dfs(int x)//求dfs序 {bg[x] = ++w;dep[x] = dep[fa[x]] + 1;change(1, 1, n, bg[x], bg[x], dep[x]);for (int i = 1; i <= 16; ++i)faa[x][i] = faa[faa[x][i - 1]][i - 1];for (int i = head[x]; i; i = a[i].next)if (!bg[a[i].to]){faa[a[i].to][0] = fa[a[i].to] = x;dfs(a[i].to);}ed[x] = w; } int lca(int x, int y) {if (dep[x] < dep[y]) swap(x, y);for (int i = 16; i >= 0; --i)if (dep[faa[x][i]] >= dep[y]) x = faa[x][i];for (int i = 16; i >= 0; --i)if (faa[x][i] != faa[y][i]) x = faa[x][i], y = faa[y][i];return x == y ? x : faa[x][0]; } int main() {scanf("%d%d", &n, &m);for (int i = 1; i < n; ++i){scanf("%d%d", &x, &y);add(x, y);add(y, x);}dfs(1);while(m--){scanf("%d", &x);if (x == 1){scanf("%d", &x);access(x);}else if (x == 2){scanf("%d%d", &x, &y);z = lca(x, y);x = ask(1, 1, n, bg[x], bg[x]);y = ask(1, 1, n, bg[y], bg[y]);z = ask(1, 1, n, bg[z], bg[z]);printf("%d\n", x + y - z * 2 + 1);}else{scanf("%d", &x);printf("%d\n", ask(1, 1, n, bg[x], ed[x]));}}return 0; }總結
以上是生活随笔為你收集整理的【线段树】【LCT】【LCA】树点涂色(luogu 3703)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 西岭雪山一日游攻略 西岭雪山一日游怎么玩
- 下一篇: 【LCT】【树状数组】Matches A