https://www.luogu.org/problemnew/show/U18430
思路比较好想
树链剖分
对于1操作
只需将以该点为根的子树打标记,将所有数存入数组排序
每次进行1操作时,判断若该点已经被标记过,则操作无效
对于操作2
在原树中求出lca,若lca被标记过,lca就是询问的两个点中
编号较小的那个,lca值不改变
对于操作3
若该点打过标记,输出该点与根的这条链上被打过标记的
深度最小节点编号a与该节点编号的差+a的深度
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; const int N = 1e6 + 10; #define yxy getchar() #define lson jd << 1 #define rson jd << 1 | 1 int n, Q, now = 1, tim, w1, w2, w3; int head[N], Num[N], deep[N], fa[N], lnum[N], rnum[N], son[N], topp[N], siz[N], tree[N], g_min[N << 2]; struct Node{int u, v, nxt;} G[N << 1]; inline int read(){ int x = 0; char c = yxy; while(c < '0' || c > '9') c = yxy; while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = yxy; return x; } inline void add(int u, int v){ G[now].u = u; G[now].v = v; G[now].nxt = head[u]; head[u] = now ++; } void dfs_1(int u, int father, int dep){ fa[u] = father; deep[u] = dep; siz[u] = 1; for(int i = head[u]; ~ i; i = G[i].nxt){ int v = G[i].v; if(v != father){ dfs_1(v, u, dep + 1); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v; } } } void dfs_2(int u, int tp){ topp[u] = tp; lnum[u] = ++ tim; tree[u] = tim; Num[tim] = u; if(!son[u]) {rnum[u] = tim; return ;} dfs_2(son[u], tp); for(int i = head[u]; ~ i; i = G[i].nxt){ int v = G[i].v; if(v != fa[u] && v != son[u]) dfs_2(v, v); } rnum[u] = tim; } void Poi_A(int l, int r, int jd, int x){ if(l == r) { if(g_min[jd]) w1 = 1; return ; } if(g_min[jd]) { g_min[lson] = g_min[jd]; g_min[rson] = g_min[jd]; } int mid = (l + r) >> 1; if(x <= mid) Poi_A(l, mid, lson, x); else Poi_A(mid + 1, r, rson, x); } void Sec_G(int l, int r, int jd, int x, int y, int yg){ if(x <= l && r <= y){ g_min[jd] = yg; return ; } int mid = (l + r) >> 1; if(x <= mid) Sec_G(l, mid, lson, x, y, yg); if(y > mid) Sec_G(mid + 1, r, rson, x, y, yg); } int calc_lca(int x, int y){ int tp1 = topp[x], tp2 = topp[y]; while(tp1 != tp2){ if(deep[tp1] < deep[tp2]) swap(tp1, tp2), swap(x, y); x = fa[tp1]; tp1 = topp[x]; } return deep[x] > deep[y] ? y : x; } void Sec_A(int l, int r, int jd, int x, int y){ if(x <= l && r <= y){ w2 = g_min[jd]; return ; } if(g_min[jd]) { g_min[lson] = g_min[jd]; g_min[rson] = g_min[jd]; } int mid = (l + r) >> 1; if(x <= mid) Sec_A(l, mid, lson, x, y); if(y > mid) Sec_A(mid + 1, r, rson, x, y); } int get_answer(int l, int r, int x){ int L = l, R = r, ret; while(L <= R){ int mid = (L + R) >> 1; if(Num[mid] <= x) L = mid + 1, ret = mid; else R = mid - 1; } return ret - l; } int main() { n = read(); Q = read(); for(int i = 1; i <= n; i ++) head[i] = -1; for(int i = 1; i < n; i ++) { int u = read(), v = read(); add(u, v); add(v, u); } dfs_1(1, 0, 1); dfs_2(1, 1); while(Q --){ int opt = read(); if(opt == 1){ int x = read(); if(!son[x]) continue ; w1 = 0; Poi_A(1, n, 1, tree[x]); if(w1) continue ; Sec_G(1, n, 1, lnum[x], rnum[x], x); sort(Num + lnum[x] + 1, Num + rnum[x] + 1); } else if(opt == 2){ int x = read(), y = read(); int Lca_now = calc_lca(x, y); w1 = 0; Poi_A(1, n, 1, tree[Lca_now]); if(w1) printf("%d ", min(x, y)); else printf("%d ", Lca_now); } else { int x = read(); w1 = 0; Poi_A(1, n, 1, tree[x]); if(!w1) printf("%d ", deep[x]); else { Sec_A(1, n, 1, lnum[x], rnum[x]); printf("%d ", get_answer(lnum[w2], rnum[w2], x) + deep[w2]); } } } return 0; }