1. 程式人生 > >HNOI2015 開店

HNOI2015 開店

一個有點權和邊權的二叉樹,多次詢問點權在 $[L,R]$ 的點到 $u$ 的距離和

$n,q \leq 100000$

sol:

1.點分治

建出分治樹的結構,考慮計算距離的過程

我們知道 $dis(u,v) = dep_u + dep_v - 2 \times dep_{lca}$

因為樹高是 logn 的,所以可以暴力爬樹高列舉 lca

把點權差分一下

對每層重心開 $3$ 個 vector 表示前 $i$ 種顏色到它的距離和,前 $i$ 種顏色到它父親的距離和,前 $i$ 種顏色的點數

因為樹高是 $O(logn)$ 的,這個空間是 $O(nlogn)$ 的

每次二分找到 $[L,R]$ 在當前 vector 裡的位置,算一下距離就可以了

順便吐槽為什麼我每次點分治都懶得寫 $O(1)$ lca

#include <bits/stdc++.h>
#define LL long long
using namespace std;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = -f;
    for (; isdigit(ch); ch = getchar()) x = 10
* x + ch - '0'; return x * f; } const int maxn = 200010; int n, q, A, yr[maxn]; int first[maxn], to[maxn << 1], nx[maxn << 1], val[maxn << 1], cnt; inline void add(int u, int v, int w) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; val[cnt] = w; } LL dis[maxn];
int fa[maxn]; namespace LCA { int dep[maxn], bl[maxn], size[maxn]; inline void dfs1(int x) { size[x] = 1; for (int i = first[x]; i; i = nx[i]) { if (to[i] == fa[x]) continue; fa[to[i]] = x; dep[to[i]] = dep[x] + 1; dis[to[i]] = dis[x] + val[i]; dfs1(to[i]); size[x] += size[to[i]]; } } inline void dfs2(int x, int col) { int k = 0; bl[x] = col; for (int i = first[x]; i; i = nx[i]) if (dep[to[i]] > dep[x] && size[to[i]] > size[k]) k = to[i]; if (!k) return; dfs2(k, col); for (int i = first[x]; i; i = nx[i]) if (dep[to[i]] > dep[x] && to[i] != k) dfs2(to[i], to[i]); } inline int lca(int x, int y) { while (bl[x] != bl[y]) { if (dep[bl[x]] < dep[bl[y]]) swap(x, y); x = fa[bl[x]]; } return dep[x] > dep[y] ? y : x; } } // namespace LCA struct Node { LL col, sum, sig, cnt; inline bool operator<(const Node &b) const { return col < b.col; } }; vector<Node> G[maxn]; inline LL caldis(int x, int y) { // cout<<dis[x] + dis[y] - 2 * dis[LCA::lca(x,y)]<<endl; if (!x || !y) return 0; return dis[x] + dis[y] - 2 * dis[LCA::lca(x, y)]; } int f[maxn], size[maxn], vis[maxn], par[maxn], sig, root; void findroot(int x, int fa) { f[x] = 0, size[x] = 1; for (int i = first[x]; i; i = nx[i]) { if (to[i] == fa || vis[to[i]]) continue; findroot(to[i], x); size[x] += size[to[i]]; f[x] = max(f[x], size[to[i]]); } f[x] = max(f[x], sig - size[x]); if (f[x] < f[root]) root = x; } void add_node(int x, int fa, int rt) { G[rt].push_back((Node){ yr[x], caldis(x, rt), (par[rt] ? caldis(x, par[rt]) : 0), 1 }); for (int i = first[x]; i; i = nx[i]) { if (to[i] == fa || vis[to[i]]) continue; add_node(to[i], x, rt); } } void build(int x) { vis[x] = 1; add_node(x, 0, x); G[x].push_back((Node){ -1, 0, 0, 0 }); sort(G[x].begin(), G[x].end()); for (int i = 1; i < G[x].size(); i++) { G[x][i].sum += G[x][i - 1].sum; G[x][i].sig += G[x][i - 1].sig; G[x][i].cnt += G[x][i - 1].cnt; } for (int i = first[x]; i; i = nx[i]) { if (vis[to[i]]) continue; root = 0; sig = size[to[i]]; findroot(to[i], 0); par[root] = x; build(root); } } LL query(int x, int ql, int qr) { LL ans = 0; for (int i = x; i; i = par[i]) { int st, ed; int l = 0, r = G[i].size() - 1; while (l <= r) { int mid = (l + r) >> 1; if (G[i][mid].col <= qr) l = mid + 1; else r = mid - 1; } ed = l - 1; l = 0, r = G[i].size() - 1; while (l <= r) { int mid = (l + r) >> 1; if (G[i][mid].col <= ql - 1) l = mid + 1; else r = mid - 1; } st = l - 1; // cout<<st<<" "<<ed<<endl; ans += (G[i][ed].sum - G[i][st].sum); if (i != x) ans += (G[i][ed].cnt - G[i][st].cnt) * caldis(i, x); if (par[i]) ans -= (G[i][ed].sig - G[i][st].sig) + (G[i][ed].cnt - G[i][st].cnt) * caldis(x, par[i]); } return ans; } int main() { n = read(), q = read(), A = read(); for (int i = 1; i <= n; i++) yr[i] = read(); for (int i = 2; i <= n; i++) { int u = read(), v = read(), w = read(); add(u, v, w); add(v, u, w); } LCA::dep[1] = 1; LCA::dfs1(1); LCA::dfs2(1, 1); sig = n; size[0] = f[0] = 2147483233; findroot(1, 0); build(root); LL lastans = 0; while (q--) { int x = read(), a = read(), b = read(); int l = min((a + lastans) % A, (b + lastans) % A); int r = max((a + lastans) % A, (b + lastans) % A); printf("%lld\n", lastans = query(x, l, r)); } }
點分治

 

2.主席樹

以點權為版本開主席樹,還是考慮計算距離,發現 $dep_u$ 和 $dep_v$ 都可以直接查,$dep_lca$ 的話,不好查

可以把所有 $v$ 到根的路徑全 $+1$,然後詢問的時候從每個 $u$ 走到根,在相應的 $v$ 的線段樹上查到根距離就可以了

主席樹跟上一種做法一樣,也是開一個關於點權字首的,查詢的時候減一下

#include <bits/stdc++.h>
#define LL long long
using namespace std;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = -f;
    for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 200010;
int n, q, A;
struct Node {
    int yr, id;
    bool operator<(const Node &b) const { return (yr == b.yr) ? (id < b.id) : (yr < b.yr); }
} ns[maxn];
struct TrNode {
    int ls, rs, tms;
    LL val;
} t[maxn << 8];
int first[maxn], to[maxn << 1], nx[maxn << 1], val[maxn << 1], cnt;
inline void add(int u, int v, int w) {
    to[++cnt] = v;
    nx[cnt] = first[u];
    first[u] = cnt;
    val[cnt] = w;
}
int ToT, root[maxn];
LL sum[maxn], dis[maxn], dsum[maxn];
int fa[maxn], pos[maxn], dfn;
int dep[maxn], bl[maxn], size[maxn];
inline void dfs1(int x) {
    size[x] = 1;
    for (int i = first[x]; i; i = nx[i]) {
        if (to[i] == fa[x])
            continue;
        fa[to[i]] = x;
        dep[to[i]] = dep[x] + 1;
        dis[to[i]] = dis[x] + val[i];
        dfs1(to[i]);
        size[x] += size[to[i]];
    }
}
inline void dfs2(int x, int col) {
    int k = 0;
    bl[x] = col;
    pos[x] = ++dfn;
    sum[dfn] = dis[x] - dis[fa[x]];
    // cout<<sum[dfn] << endl;
    for (int i = first[x]; i; i = nx[i])
        if (dep[to[i]] > dep[x] && size[to[i]] > size[k])
            k = to[i];
    if (!k)
        return;
    dfs2(k, col);
    for (int i = first[x]; i; i = nx[i])
        if (dep[to[i]] > dep[x] && to[i] != k)
            dfs2(to[i], to[i]);
}
inline void build(int &x, int l, int r) {
    x = ++ToT;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    build(t[x].ls, l, mid);
    build(t[x].rs, mid + 1, r);
}
inline void Insert(int &x, int l, int r, int L, int R) {
    t[++ToT] = t[x];
    if (L <= l && r <= R) {
        t[x = ToT].tms++;
        return;
    }
    t[x = ToT].val += sum[min(R, r)] - sum[max(l - 1, L - 1)];
    int mid = (l + r) >> 1; /*
     if (R <= mid) Insert(t[x].ls,l,mid,L,R);
     else if (L > mid) Insert(t[x].rs,mid + 1,r,L,R);
     else Insert(t[x].ls,l,mid,L,mid),Insert(t[x].rs,mid + 1,r,mid + 1,R);*/
    if (L <= mid)
        Insert(t[x].ls, l, mid, L, R);
    if (R > mid)
        Insert(t[x].rs, mid + 1, r, L, R);
}
inline LL query(int x, int l, int r, int L, int R) {
    LL res = 1LL * (sum[min(R, r)] - sum[max(l - 1, L - 1)]) * t[x].tms;
    if (L <= l && r <= R)
        return res + t[x].val;
    int mid = (l + r) >> 1; /*
    if (R <= mid) return res + query(t[x].ls,l,mid,L,R);
    else if (L > mid) return res + query(t[x].rs,mid + 1,r,L,R);
    else return res + query(t[x].ls,l,mid,L,mid) + query(t[x].rs,mid + 1,r,mid + 1,R);*/
    if (L <= mid)
        res += query(t[x].ls, l, mid, L, R);
    if (R > mid)
        res += query(t[x].rs, mid + 1, r, L, R);
    return res;
}
inline LL ask(int u, int v) {
    LL res = 0;
    while (bl[u] != 1) {
        res += query(root[v], 1, n, pos[bl[u]], pos[u]);
        u = fa[bl[u]];
    }
    res += query(root[v], 1, n, 1, pos[u]);
    return res;
}
inline void add(int u, int v) {
    while (bl[u] != 1) {
        Insert(root[v], 1, n, pos[bl[u]], pos[u]);
        u = fa[bl[u]];
    }
    Insert(root[v], 1, n, 1, pos[u]);
}
int main() {
    n = read(), q = read(), A = read();
    for (int i = 1; i <= n; i++) ns[i].yr = read(), ns[i].id = i;
    sort(ns + 1, ns + n + 1);
    for (int i = 2; i <= n; i++) {
        int u = read(), v = read(), w = read();
        add(u, v, w);
        add(v, u, w);
    }
    dfs1(1);
    dfs2(1, 1);
    for (int i = 1; i <= n; i++) sum[i] += sum[i - 1], dsum[i] = dsum[i - 1] + dis[ns[i].id];
    build(root[0], 1, n);
    for (int i = 1; i <= n; i++) {
        int u = ns[i].id;
        root[i] = root[i - 1];
        while (bl[u] != 1) {
            Insert(root[i], 1, n, pos[bl[u]], pos[u]);
            u = fa[bl[u]];
        }
        Insert(root[i], 1, n, 1, pos[u]);
    }
    LL lastans = 0;
    while (q--) {
        int u = read(), a = read(), b = read();
        int l = min((a + lastans) % A, (b + lastans) % A);
        int r = max((a + lastans) % A, (b + lastans) % A);
        l = lower_bound(ns + 1, ns + n + 1, (Node){ l, 0 }) - ns;
        r = upper_bound(ns + 1, ns + n + 1, (Node){ r, n }) - ns - 1;
        // cout<<l<<" "<<r<<endl;
        printf("%lld\n", lastans = 1LL * (r - l + 1) * dis[u] + dsum[r] - dsum[l - 1] -
                                   2 * (ask(u, r) - ask(u, l - 1)));
    }
}
主席樹