• HNOI2015 开店


    一个有点权和边权的二叉树,多次询问点权在 $[L,R]$ 的点到 $u$ 的距离和

    $n,q leq 100000$

    sol:

    1.点分治

    建出分治树的结构,考虑计算距离的过程

    我们知道 $dis(u,v) = dep_u + dep_v - 2 imes 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
    ", 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
    ", lastans = 1LL * (r - l + 1) * dis[u] + dsum[r] - dsum[l - 1] -
                                       2 * (ask(u, r) - ask(u, l - 1)));
        }
    }
    主席树
  • 相关阅读:
    STOAdiary20110312抉择
    STOA静夜思
    STOAOO
    STOAdiary20110316翻译的一天
    STOAdiary20110318疲倦与激情
    STOAdiary20110314平静的一天
    Silverlight入门
    Postgres远程访问配置
    [收藏]Jquery客户端拖拽排序方法
    整理的邮件一个邮件发送类
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/10214762.html
Copyright © 2020-2023  润新知