洛谷P3248 [HNOI2016]樹(主席樹 倍增 )
阿新 • • 發佈:2019-02-07
else https line [1] read 根節點 ref fin tin
題意
題目鏈接
Sol
從上午九點淦到現在qwq
思路比較簡單,就是把每次加入的一坨點看成一個,然後直接倍增搞。。
然後慢慢調就可以了。。。
最後數量級會到達\(10^{10}\),所以應該開long long
#include<bits/stdc++.h> #define Pair pair<LL, LL> #define MP make_pair #define fi first #define se second #define LL long long #define int long long using namespace std; const int MAXN = 1e5 + 10, B = 17, SS = 7e6 + 10; inline LL read() { char c = getchar(); LL x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N, M, Q; vector<int> v[MAXN]; int siz[MAXN], top[MAXN], son[MAXN], fa[MAXN], ID[MAXN], rev[MAXN], tim; LL dep[MAXN]; void dfs1(int x, int _fa) { siz[x] = 1; dep[x] = dep[_fa] + 1; fa[x] = _fa; ID[x] = ++tim; rev[tim] = x; for(auto &to : v[x]) { if(to == _fa) continue; dfs1(to, x); siz[x] += siz[to]; if(siz[to] > siz[son[x]]) son[x] = to; } } void dfs2(int x, int topf) { top[x] = topf; if(!son[x]) return ; dfs2(son[x], topf); for(auto &to : v[x]) { if(top[to]) continue; dfs2(to, to); } } int LCA(int x, int y) { while(top[x] ^ top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); x = fa[top[x]]; } if(dep[x] < dep[y]) swap(x, y); return y; } LL GetDis(int x, int y) { int lca = LCA(x, y); return dep[x] + dep[y] - 2 * dep[lca]; } int tot; struct Ope { int l, r, id, fa, ti;//id所在的根節點是哪個,fa連到了大樹哪個節點下面 ti第幾次操作 bool operator < (const Ope &rhs) const { return r < rhs.r; } }md[MAXN]; set<Ope> lin; int root[SS], si[SS], ls[SS], rs[SS], cnt; void insert(int &k, int pre, int l, int r, int v) { k = ++cnt; ls[k] = ls[pre]; rs[k] = rs[pre]; si[k] = si[pre] + 1; if(l == r) return; int mid = (l + r) >> 1; if(v <= mid) insert(ls[k], ls[pre], l, mid, v); else insert(rs[k], rs[pre], mid + 1, r, v); } int Query(int tl, int tr, int l, int r, int k) { if(l == r) return l; int cur = si[ls[tr]] - si[ls[tl]], mid = (l + r) >> 1; if(cur >= k) return Query(ls[tl], ls[tr], l, mid, k); else return Query(rs[tl], rs[tr], mid + 1, r, k - cur); } int Kth(int x, int k) {//以x節點為根的子樹中第k小的節點 int l = ID[x], r = ID[x] + siz[x] - 1; int tmp = Query(root[l - 1], root[r], 1, N, k); //printf("%d %d %d\n", x, k, tmp); return tmp; } Pair GetId(int x) {//大樹中編號為x節點對應的小樹中的節點編號,以及該節點被加入的時間 Ope nl = *lin.lower_bound((Ope){0, x, 0, 0, 0}); return {Kth(nl.id, x - nl.l + 1), nl.ti}; } vector<int> V[MAXN]; int Fa[MAXN][B + 1]; LL Dep[MAXN], Dis[MAXN][B + 1], t[MAXN][B + 1]; void Dfs(int x, int fa) { Dep[x] = Dep[fa] + 1; for(auto &to : V[x]) { if(to == fa) continue; Dfs(to, x); } } void Pre() { for(int j = 1; j <= B; j++) for(int i = 1; i <= M; i++) { Fa[i][j] = Fa[Fa[i][j - 1]][j - 1]; t[i][j] = t[Fa[i][j - 1]][j - 1]; Dis[i][j] = Dis[i][j - 1] + Dis[Fa[i][j - 1]][j - 1]; } } LL calc(int x) {//計算大樹中編號為x的節點到所在大節點的根的距離 Pair tmp = GetId(x); return dep[tmp.fi] - dep[md[tmp.se].id]; } LL Query(LL x, LL y) {//大樹中編號為x, y的節點的距離 Pair bx = GetId(x), by = GetId(y); if(bx.se == by.se) return GetDis(bx.fi, by.fi); LL prex = x, prey = y, gx, gy; x = bx.se; y = by.se; if(Dep[x] < Dep[y]) swap(x, y), swap(prex, prey); LL ans = calc(prex); for(int i = B; ~i; i--) if(Dep[Fa[x][i]] >= Dep[y]) { if(Fa[x][i] == y) { gx = GetId(t[x][i]).fi; gy = GetId(prey).fi; return ans + Dis[x][i] + GetDis(gx, gy) - (dep[gx] - dep[md[Fa[x][i]].id]); } ans += Dis[x][i], x = Fa[x][i]; } // if(x == y) return ans - 2 * calc(prey); for(int i = B; ~i; i--) if(Fa[x][i] != Fa[y][i]) { ans += Dis[x][i]; ans += Dis[y][i]; x = Fa[x][i], y = Fa[y][i]; } gx = GetId(md[x].fa).fi, gy = GetId(md[y].fa).fi; return ans + 2 + GetDis(gx, gy) + calc(prey); } signed main() { //freopen("tree13.in", "r", stdin);freopen("b.out", "w", stdout); N = read(); M = read() + 1; Q = read(); for(int i = 1; i <= N - 1; i++) { int x = read(), y = read(); v[x].push_back(y); v[y].push_back(x); } dfs1(1, 0); dfs2(1, 1);//樹剖 for(int i = 1; i <= N; i++) insert(root[i], root[i - 1], 1, N, rev[i]); md[1] = {1, siz[1], 1, 0, 1}; lin.insert(md[1]); tot = siz[1] + 1; for(int i = 2; i <= M; i++) { int x = read(), to = read(); md[i] = {tot, tot + siz[x] - 1, x, to, i}; lin.insert(md[i]); tot += siz[x]; } for(int i = 2; i <= M; i++) { Ope x = md[i]; int u = x.ti, v = GetId(x.fa).se;//大樹以操作次序來標號 V[v].push_back(u); Dis[u][0] = dep[GetId(md[u].fa).fi] - dep[md[v].id] + 1; Fa[u][0] = v; t[u][0] = md[u].fa; } Dfs(1, 0); Pre(); while(Q--) { LL x = read(), y = read(); cout << Query(x, y) << '\n'; } return 0; }
洛谷P3248 [HNOI2016]樹(主席樹 倍增 )