【题解】HNOI2016树
大概最近写的这些题目都是仿生的代码……在这里先说明一下。可能比起做题记录来说更加像是学习笔记吧。之所以这样做主要还是因为感受到最近做的很多题目自己会做的都比较简单,不会做的又不敢触及,虽然也有所进步、学习了一些新的算法,但大体而言还是在原地踏步的样子。于是想要多观摩一下他人做题的过程并加以记录,也能开拓自己的视野,重新整理出新的思路,学习新的代码技巧。
这一道题目首先第一眼我们就可以放弃建出这棵树的想法:极端情况下树的节点可以达到n2的级别,1e10个节点光是建出来就已经不可接受,更别谈找lca求距离了。但我们注意到大树上的每一棵小树都是模板树的一部分,如果说只将每次复制的新树的根看做节点的话,本质不同的点最多也只有O(n)个。这里我们就形成了思路:将每一次复制出来的子树看做一个节点挂在大树上,形成一个树套树的结构。这样,我们可以利用倍增快速求出大树上节点与节点之间的距离,而位于小块(小模板树)内部的距离我们则单独处理。
其实感觉有点类似分块的呀,大块就直接跳,小块暴力 : ) (代码仿生)
#include <bits/stdc++.h> using namespace std; #define maxn 100005 #define int long long int n, m, q; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } namespace Small { int cnp = 1, cnt, tot, head[maxn], gra[maxn][19], a[maxn]; int dep[maxn], root[maxn], L[maxn], R[maxn]; struct node { int last, to; }E[maxn << 1]; struct tree { int l, r, size; }T[maxn * 20]; void add(int u, int v) { E[cnp].to = v, E[cnp].last = head[u], head[u] = cnp ++; } void dfs(int u) { L[u] = ++ cnt; a[cnt] = u; for(int i = 1; i < 19; i ++) gra[u][i] = gra[gra[u][i - 1]][i - 1]; for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(v == gra[u][0]) continue; dep[v] = dep[u] + 1; gra[v][0] = u; dfs(v); } R[u] = cnt; } void update(int &now, int pre, int l, int r, int key) { now = ++ tot; T[now] = T[pre]; T[now].size ++; if(l == r) return; int mid = (l + r) >> 1; if(key <= mid) update(T[now].l, T[pre].l, l, mid, key); else update(T[now].r, T[pre].r, mid + 1, r, key); } void Build() { for(int i = 1; i <= n; i ++) update(root[i], root[i - 1], 1, n, a[i]); } void work() { for(int i = 1; i < n; i ++) { int u = read(), v = read(); add(u, v), add(v, u); } dep[1] = 0; dfs(1); Build(); } int Size(int x) { return R[x] - L[x] + 1; } int query(int a, int b, int l, int r, int k) { if(l == r) return l; int mid = (l + r) >> 1, size = T[T[b].l].size - T[T[a].l].size; if(size >= k) return query(T[a].l, T[b].l, l, mid, k); else return query(T[a].r, T[b].r, mid + 1, r, k - size); } int ask(int rt, int k) { int l = L[rt], r = R[rt]; return query(root[l - 1], root[r], 1, n, k); } int to(int a, int b) { return dep[a] - dep[b]; } int lca(int u, int v) { if(dep[u] < dep[v]) swap(u, v); for(int i = 18; ~i; i --) if(dep[gra[u][i]] >= dep[v]) u = gra[u][i]; for(int i = 18; ~i; i --) if(gra[u][i] != gra[v][i]) u = gra[u][i], v = gra[v][i]; return u == v ? u : gra[u][0]; } int dis(int u, int v) { int LCA = lca(u, v); return dep[u] + dep[v] - 2 * dep[LCA]; } } namespace Big { int up, tnum, tl[maxn], tr[maxn]; int dep[maxn], t_to[maxn], trt[maxn]; int c[maxn][19], gra[maxn][19]; int pos(int x) { int l = 1, r = tnum; while(l < r) { int mid = (l + r) >> 1; if(x < tl[mid]) r = mid - 1; else if(x > tr[mid]) l = mid + 1; else return mid; } return l; } void work() { up = n, tnum = 1, tl[1] = 1, tr[1] = n, trt[1] = 1; for(int i = 1; i <= m; i ++) { int a = read(), b = read(); int P = pos(b); trt[++ tnum] = a; tl[tnum] = up + 1; up += Small :: Size(a); tr[tnum] = up; gra[tnum][0] = P; dep[tnum] = dep[P] + 1; t_to[tnum] = Small :: ask(trt[P], b - tl[P] + 1); // 得到了b的编号 c[tnum][0] = Small :: to(t_to[tnum], trt[P]) + 1; } for(int j = 1; j < 18; j ++) for(int i = 1; i <= tnum; i ++) { gra[i][j] = gra[gra[i][j - 1]][j - 1]; c[i][j] = c[i][j - 1] + c[gra[i][j - 1]][j - 1]; } for(int i = 1; i <= q; i ++) { int u = read(), v = read(), ret = 0; int posu = pos(u), posv = pos(v); if(dep[posu] < dep[posv]) swap(posu, posv), swap(u, v); int reu = Small :: ask(trt[posu], u - tl[posu] + 1); int rev = Small :: ask(trt[posv], v - tl[posv] + 1); if(posu == posv) { printf("%lld\n", Small :: dis(rev, reu)); continue; } u = posu, v = posv; for(int i = 18; ~i; i --) if(dep[gra[u][i]] > dep[v]) ret += c[u][i], u = gra[u][i]; if(gra[u][0] == v) { ret += 1; u = t_to[u]; v = rev; ret += Small :: dis(u, v); ret += Small :: to(reu, trt[posu]); printf("%lld\n", ret); continue; } if(dep[u] > dep[v]) ret += c[u][0], u = gra[u][0]; for(int i = 18; ~i; i --) if(gra[u][i] != gra[v][i]) { ret += c[u][i], ret += c[v][i]; u = gra[u][i], v = gra[v][i]; } ret += 2; ret += Small :: dis(t_to[u], t_to[v]); ret += Small :: dis(reu, trt[posu]) + Small :: dis(rev, trt[posv]); printf("%lld\n", ret); } } } signed main() { n = read(), m = read(), q = read(); Small :: work(); Big :: work(); return 0; }