1. 程式人生 > 其它 >Java之JDBC(資料庫連線)

Java之JDBC(資料庫連線)

樹鏈剖分

樹鏈剖分的作用:

​ 將樹轉化為序列,這樣樹中任意一條路徑就可以對應到最多$O(logn)$段的連續區間然後就可以用維護區間的樹據結構來維護了

一些定義:

  1. 輕/重兒子:當前結點的子節點中子樹最大的結點就是重兒子,其它的都是輕兒子

  2. 輕/重邊:當前結點到重兒子的邊為重邊,到輕兒子的邊叫輕邊

  3. 重鏈:由重邊構成的極大路徑

$dfs$序:優先遍歷重兒子,保證重鏈上的點的編號都是連續的,方便維護區間

定理:樹中任意一條路徑均可拆分成$O(logn)$個連續區間/條重鏈

為什麼是$logn$個區間 ?

每次走輕兒子至少會將子樹的結點減少一半,因此最多走$logn$次輕兒子之後就是公共的重鏈了。

如何將一條路徑拆分成$logn$個區間?

通過重鏈往上爬,找到最近公共重鏈,最後加上相同重鏈裡的間隔部分

修改這些區間維護的資訊的複雜度為$O(logn)$ 對於$logn$個區間單次詢問的複雜度上界就是$log^2n$

具體步驟

  1. 預處理所有結點的重兒子以及子樹內的節點的個數和每個節點的父節點
void dfs1(int u, int fas, int deps) {
	dep[u] = deps, fa[u] = fas, sz[u] = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fas) continue ;
        dfs(j, u, deps + 1);
        sz[u] += sz[j];
        if (sz[son[u]] < sz[j]) u = j; // 預處理重兒子
    }
}
  1. 樹鏈剖分,找出每個結點所屬重鏈的頂點,$dfs$序的編號,建立$u$到$id$的$w$對映(樹結點對應的序列位置的對映)
void dfs2(int u, int t) {// 做剖分, t是當前重鏈的頂點,輕鏈的頂點為起本身
    id[u] = ++ cnt, nw[cnt] = w[u], top[u] = t;
    if (!sno[u]) return ;// 無重兒子即葉子結點直接返回即可
    dfs2(sno[u], t); // 優先重兒子
    for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
        if (j == sno[u]) continue ;
        dfs2(j, j); //輕兒子的重鏈的頂點就是自己
    }
}
  • 維護樹上兩個點的路徑
void update_path(int u, int v, int k) { // u -> v路徑所有結點加k
   while (top[u] != top[v]) {
   	if (dep[top[u]] < dep[top[v]]) swap(u, v);
       update(1, id[top[u]], id[u], k);
       u = fa[top[u]];
   }
   if (dep[u] < dep[v]) swap(u, v);
   update(1, id[v], id[u], k);

}
  • 維護以一個結點為根節點的子樹
void update_tree(int u, int ) { // 子樹全部加上k
	update(1, id[u], id[u] + sz[u] - 1, k); 
// 由於dfs序所以編號連續 所以直接從st + size 就可以推斷
}

板子題

[Link](2568. 樹鏈剖分 - AcWing題庫)

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
int id[N], nw[N], cnt;
int dep[N], sz[N], top[N], fa[N], son[N];
int n, m, k;
struct Node {
    int l, r;
    LL sum, tag;
}tr[N << 2];

void dfs1(int u, int fas, int deps) {
    dep[u] = deps, sz[u] = 1, fa[u] = fas;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fas) continue ;
        dfs1(j, u, deps + 1);
        sz[u] += sz[j];
        if (sz[son[u]] < sz[j]) son[u] = j;
    }
}
void dfs2(int u, int t) {
    id[u] = ++cnt, nw[cnt] = w[u], top[u] = t;
    if (!son[u]) return ;
    dfs2(son[u], t);
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == son[u] || j == fa[u]) continue ;
        dfs2(j, j);
    }
}
void pushup(int u) {
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(Node& rt, Node& lc, Node& rc) {
    if (rt.tag) {
        lc.sum += rt.tag * (lc.r - lc.l + 1);
        rc.sum += rt.tag * (rc.r - rc.l + 1);
        rc.tag += rt.tag, lc.tag += rt.tag;
        rt.tag = 0;
    }
}
void build(int u, int l, int r) {
    tr[u] = {l, r, nw[r], 0};
    if (l == r) return ;
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
} 
void update(int u, int l, int r, int k) {
    if (l <= tr[u].l && tr[u].r <= r) {
        tr[u].sum += (tr[u].r - tr[u].l + 1) * k;
        tr[u].tag += k;
        return ;
    }
    pushdown(tr[u], tr[u << 1], tr[u << 1 | 1]);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) update(u << 1, l, r, k);
    if (r > mid) update(u << 1 | 1, l, r, k);
    pushup(u);
}
LL query(int u, int l, int r) {
    if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
    pushdown(tr[u], tr[u << 1], tr[u << 1 | 1]);
    LL res = 0;
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) res = query(u << 1, l, r);
    if (r > mid) res += query(u << 1 | 1, l, r);
    return res;
}
void update_path(int u, int v, int k) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        update(1, id[top[u]], id[u], k);
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    update(1, id[v], id[u], k);
}
LL query_path(int u, int v) {
    LL res = 0;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        res += query(1, id[top[u]], id[u]);
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    res += query(1, id[v], id[u]);
    return res;
}
void update_tree(int u, int k) {
    update(1, id[u], id[u] + sz[u] - 1, k);
}
LL query_tree(int u) {
    return query(1, id[u], id[u] + sz[u] - 1);
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++) cin >> w[i];
    for (int i = 1; i < n; i ++) {
        int x, y; cin >> x >> y;
        add(x, y), add(y, x);
    }
    dfs1(1, -1, 1);
    dfs2(1, 1);
    build(1, 1, n);
    cin >> m;
    while (m --) {
        int t, u, v, k;
        cin >> t >> u;
        if (t == 1) {
            cin >> v >> k;
            update_path(u, v, k);
        }
        else if (t == 2) {
            cin >> k;
            update_tree(u, k);
        }
        else if (t == 3) {
            cin >> v;
            cout << query_path(u, v) << '\n';
        }
        else cout << query_tree(u) << '\n';
    }
    return 0;
}